using System;
using System.Collections.Generic;
using System.IO;
using java.util;
namespace org.jawk.frontend
{
	/// <summary>
	/// AwkParser - converts the AWK script into a syntax tree
	/// useful to either backend that compiles the script or interprets
	/// the script.
	/// </summary>
	/// <remarks>
	/// AwkParser - converts the AWK script into a syntax tree
	/// useful to either backend that compiles the script or interprets
	/// the script.
	/// <p>
	/// It contains the internal state of the parser and the lexer.
	/// </remarks>
	public class AwkParser
	{
		/// <summary>
		/// Interface for statement AST nodes that can be interrupted
		/// with a break statement.
		/// </summary>
		/// <remarks>
		/// Interface for statement AST nodes that can be interrupted
		/// with a break statement.
		/// </remarks>
		private interface IBreakable
		{
			// for NULL_OFFSET
			org.jawk.intermediate.IAddress BreakAddress();
		}

		/// <summary>
		/// Interface for statement AST nodes that can be entered
		/// via a next statement.
		/// </summary>
		/// <remarks>
		/// Interface for statement AST nodes that can be entered
		/// via a next statement.
		/// </remarks>
		private interface INextable
		{
			org.jawk.intermediate.IAddress NextAddress();
		}

		/// <summary>
		/// Interface for statement AST nodes that can be re-entered
		/// with a continue statement.
		/// </summary>
		/// <remarks>
		/// Interface for statement AST nodes that can be re-entered
		/// with a continue statement.
		/// </remarks>
		private interface IContinueable
		{
			org.jawk.intermediate.IAddress ContinueAddress();
		}

		/// <summary>Lexer token values, similar to yytok values in lex/yacc.</summary>
		/// <remarks>Lexer token values, similar to yytok values in lex/yacc.</remarks>
		private static int s_idx = 257;

		private static readonly int _EOF_ = s_idx++;

		private static readonly int _NEWLINE_ = s_idx++;

		private static readonly int _SEMICOLON_ = s_idx++;

		private static readonly int _ID_ = s_idx++;

		private static readonly int _FUNC_ID_ = s_idx++;

		private static readonly int _INTEGER_ = s_idx++;

		private static readonly int _DOUBLE_ = s_idx++;

		private static readonly int _STRING_ = s_idx++;

		private static readonly int _EQUALS_ = s_idx++;

		private static readonly int _AND_ = s_idx++;

		private static readonly int _OR_ = s_idx++;

		private static readonly int _EQ_ = s_idx++;

		private static readonly int _GT_ = s_idx++;

		private static readonly int _GE_ = s_idx++;

		private static readonly int _LT_ = s_idx++;

		private static readonly int _LE_ = s_idx++;

		private static readonly int _NE_ = s_idx++;

		private static readonly int _NOT_ = s_idx++;

		private static readonly int _PIPE_ = s_idx++;

		private static readonly int _QUESTION_MARK_ = s_idx++;

		private static readonly int _COLON_ = s_idx++;

		private static readonly int _APPEND_ = s_idx++;

		private static readonly int _PLUS_ = s_idx++;

		private static readonly int _MINUS_ = s_idx++;

		private static readonly int _MULT_ = s_idx++;

		private static readonly int _DIVIDE_ = s_idx++;

		private static readonly int _MOD_ = s_idx++;

		private static readonly int _POW_ = s_idx++;

		private static readonly int _COMMA_ = s_idx++;

		private static readonly int _MATCHES_ = s_idx++;

		private static readonly int _NOT_MATCHES_ = s_idx++;

		private static readonly int _DOLLAR_ = s_idx++;

		private static readonly int _INC_ = s_idx++;

		private static readonly int _DEC_ = s_idx++;

		private static readonly int _PLUS_EQ_ = s_idx++;

		private static readonly int _MINUS_EQ_ = s_idx++;

		private static readonly int _MULT_EQ_ = s_idx++;

		private static readonly int _DIV_EQ_ = s_idx++;

		private static readonly int _MOD_EQ_ = s_idx++;

		private static readonly int _POW_EQ_ = s_idx++;

		private static readonly int _OPEN_PAREN_ = s_idx++;

		private static readonly int _CLOSE_PAREN_ = s_idx++;

		private static readonly int _OPEN_BRACE_ = s_idx++;

		private static readonly int _CLOSE_BRACE_ = s_idx++;

		private static readonly int _OPEN_BRACKET_ = s_idx++;

		private static readonly int _CLOSE_BRACKET_ = s_idx++;

		private static readonly int _BUILTIN_FUNC_NAME_ = s_idx++;

		private static readonly int _EXTENSION_ = s_idx++;

		/// <summary>
		/// Contains a mapping of Jawk keywords to their
		/// token values.
		/// </summary>
		/// <remarks>
		/// Contains a mapping of Jawk keywords to their
		/// token values.
		/// They closely correspond to AWK keywords, but with
		/// a few added extensions.
		/// <p>
		/// Keys are the keywords themselves, and values are the
		/// token values (equivalent to yytok values in lex/yacc).
		/// <p>
		/// <strong>Note:</strong> whether built-in AWK function names
		/// and special AWK variable names are formally keywords or not,
		/// they are not stored in this map.  They are separated
		/// into other maps.
		/// </remarks>
		private static readonly System.Collections.Generic.IDictionary<string, int> KEYWORDS
			 = new System.Collections.Generic.Dictionary<string, int>();

		static AwkParser()
		{
			
			// Lexable tokens...
			// special keywords
			KEYWORDS["function"] = s_idx++;
			KEYWORDS["BEGIN"] = s_idx++;
			KEYWORDS["END"] = s_idx++;
			KEYWORDS["in"] = s_idx++;
			// statements
			KEYWORDS["if"] = s_idx++;
			KEYWORDS["else"] = s_idx++;
			KEYWORDS["while"] = s_idx++;
			KEYWORDS["for"] = s_idx++;
			KEYWORDS["do"] = s_idx++;
			KEYWORDS["return"] = s_idx++;
			KEYWORDS["exit"] = s_idx++;
			KEYWORDS["next"] = s_idx++;
			KEYWORDS["continue"] = s_idx++;
			KEYWORDS["delete"] = s_idx++;
			KEYWORDS["break"] = s_idx++;
			// special-form functions
			KEYWORDS["print"] = s_idx++;
			KEYWORDS["printf"] = s_idx++;
			KEYWORDS["getline"] = s_idx++;

            
            BUILTIN_FUNC_NAMES["atan2"] = f_idx++;
            BUILTIN_FUNC_NAMES["close"] = f_idx++;
            BUILTIN_FUNC_NAMES["cos"] = f_idx++;
            BUILTIN_FUNC_NAMES["exp"] = f_idx++;
            BUILTIN_FUNC_NAMES["index"] = f_idx++;
            BUILTIN_FUNC_NAMES["int"] = f_idx++;
            BUILTIN_FUNC_NAMES["length"] = f_idx++;
            BUILTIN_FUNC_NAMES["log"] = f_idx++;
            BUILTIN_FUNC_NAMES["match"] = f_idx++;
            BUILTIN_FUNC_NAMES["rand"] = f_idx++;
            BUILTIN_FUNC_NAMES["sin"] = f_idx++;
            BUILTIN_FUNC_NAMES["split"] = f_idx++;
            BUILTIN_FUNC_NAMES["sprintf"] = f_idx++;
            BUILTIN_FUNC_NAMES["sqrt"] = f_idx++;
            BUILTIN_FUNC_NAMES["srand"] = f_idx++;
            BUILTIN_FUNC_NAMES["sub"] = f_idx++;
            BUILTIN_FUNC_NAMES["gsub"] = f_idx++;
            BUILTIN_FUNC_NAMES["substr"] = f_idx++;
            BUILTIN_FUNC_NAMES["system"] = f_idx++;
            BUILTIN_FUNC_NAMES["tolower"] = f_idx++;
            BUILTIN_FUNC_NAMES["toupper"] = f_idx++;

            
            SPECIAL_VAR_NAMES["NR"] = sp_idx;
            SPECIAL_VAR_NAMES["FNR"] = sp_idx;
            SPECIAL_VAR_NAMES["NF"] = sp_idx;
            SPECIAL_VAR_NAMES["FS"] = sp_idx;
            SPECIAL_VAR_NAMES["RS"] = sp_idx;
            SPECIAL_VAR_NAMES["OFS"] = sp_idx;
            SPECIAL_VAR_NAMES["RSTART"] = sp_idx;
            SPECIAL_VAR_NAMES["RLENGTH"] = sp_idx;
            SPECIAL_VAR_NAMES["FILENAME"] = sp_idx;
            SPECIAL_VAR_NAMES["SUBSEP"] = sp_idx;
            SPECIAL_VAR_NAMES["CONVFMT"] = sp_idx;
            SPECIAL_VAR_NAMES["OFMT"] = sp_idx;
            SPECIAL_VAR_NAMES["ENVIRON"] = sp_idx;
            SPECIAL_VAR_NAMES["ARGC"] = sp_idx;
            SPECIAL_VAR_NAMES["ARGV"] = sp_idx;
		}

		/// <summary>Built-in function token values.</summary>
		/// <remarks>
		/// Built-in function token values.
		/// Built-in function token values are distinguished
		/// from lexer token values.
		/// </remarks>
		private static int f_idx = 257;

		/// <summary>
		/// A mapping of built-in function names to their
		/// function token values.
		/// </summary>
		/// <remarks>
		/// A mapping of built-in function names to their
		/// function token values.
		/// <p>
		/// <strong>Note:</strong> these are not lexer token
		/// values.  Lexer token values are for keywords and
		/// operators.
		/// </remarks>
		private static readonly System.Collections.Generic.IDictionary<string, int> BUILTIN_FUNC_NAMES
			 = new System.Collections.Generic.Dictionary<string, int>();


		private const int sp_idx = 257;

		/// <summary>
		/// Contains a mapping of Jawk special variables to their
		/// variable token values.
		/// </summary>
		/// <remarks>
		/// Contains a mapping of Jawk special variables to their
		/// variable token values.
		/// As of this writing, they correpond exactly to
		/// standard AWK variables, no more, no less.
		/// <p>
		/// Keys are the variable names themselves, and values are the
		/// variable token values.
		/// </remarks>
		private static readonly System.Collections.Generic.IDictionary<string, int> SPECIAL_VAR_NAMES
			 = new System.Collections.Generic.Dictionary<string, int>();

		
		/// <summary>
		/// Defined as concrete implementation class (not an
		/// interface reference) as to not clutter the interface
		/// with methods appropriate for private access, only.
		/// </summary>
		/// <remarks>
		/// Defined as concrete implementation class (not an
		/// interface reference) as to not clutter the interface
		/// with methods appropriate for private access, only.
		/// </remarks>
		private readonly org.jawk.frontend.AwkParser.AwkSymbolTableImpl symbol_table;

		private readonly bool additional_functions;

		private readonly bool additional_type_functions;

		private readonly bool no_input;

		private readonly System.Collections.Generic.IDictionary<string, org.jawk.ext.IJawkExtension
			> extensions;

		public AwkParser(bool additional_functions, bool additional_type_functions, bool 
			no_input, System.Collections.Generic.IDictionary<string, org.jawk.ext.IJawkExtension
			> extensions)
		{
			symbol_table = new org.jawk.frontend.AwkParser.AwkSymbolTableImpl(this);
			this.additional_functions = additional_functions;
			this.additional_type_functions = additional_type_functions;
			this.no_input = no_input;
			if (additional_functions)
			{
				// HACK : When recompiling via exec(),
				// this code is executed more than once.
				// As a result, guard against polluting the
				// KEYWORDS with different symbol ids.
				// etc.
				if (!KEYWORDS.ContainsKey("_sleep"))
				{
					// Must not be reentrant!
					// (See hack notice above.)
					//assert KEYWORDS.get("_sleep") == null;
					//assert KEYWORDS.get("_dump") == null;
					KEYWORDS["_sleep"] = s_idx++;
					KEYWORDS["_dump"] = s_idx++;
					BUILTIN_FUNC_NAMES["exec"] = f_idx++;
				}
			}
			if (additional_type_functions)
			{
				if (!KEYWORDS.ContainsKey("_INTEGER"))
				{
					// Must not be reentrant!
					// (See hack notice above.)
					//assert KEYWORDS.get("_INTEGER") == null;
					//assert KEYWORDS.get("_DOUBLE") == null;
					//assert KEYWORDS.get("_STRING") == null;
					KEYWORDS["_INTEGER"] = s_idx++;
					KEYWORDS["_DOUBLE"] = s_idx++;
					KEYWORDS["_STRING"] = s_idx++;
				}
			}
			// Special handling for exec().
			// Need to keep "extensions" around only
			// for exec().  But, must assign it regardless
			// even if "additional_functions" is not true
			// because it is a final field variable.
			this.extensions = extensions;
		}

        private LineNumberTextReader reader;

		private int c;

		private int token;

		private System.Text.StringBuilder text = new System.Text.StringBuilder();

		private System.Text.StringBuilder @string = new System.Text.StringBuilder();

		private System.Text.StringBuilder regexp = new System.Text.StringBuilder();

		private int chr;

		/// <exception cref="System.IO.IOException"></exception>
		private void Read()
		{
			text.Append((char)c);
			c = reader.Read();
			// completely bypass \r's
			while (c == '\r')
			{
				c = reader.Read();
			}
			if (c < 0)
			{
				chr = 0;
			}
			else
			{
				// eof
				chr = c;
			}
		}

		/// <summary>Parse the script streamed by script_reader.</summary>
		/// <remarks>
		/// Parse the script streamed by script_reader.  Build and return the
		/// root of the abstract syntax tree which represents the Jawk script.
		/// </remarks>
		/// <param name="script_reader">The Reader streaming the script to parse.</param>
		/// <returns>The abstract syntax tree of this script.</returns>
		/// <exception cref="System.IO.IOException">upon an IO error.</exception>
		public virtual org.jawk.frontend.IAwkSyntaxTree Parse(TextReader script_reader)
		{
			reader = new LineNumberTextReader(script_reader);
			Read();
			Lexer();
			return SCRIPT();
		}

		[System.Serializable]
		private class LexerException : System.IO.IOException
		{
			internal LexerException(AwkParser _enclosing, string msg) : base(msg + " (line " + _enclosing.reader.Line + ")")
			{
				this._enclosing = _enclosing;
			}

			private readonly AwkParser _enclosing;
		}

		/// <exception cref="System.IO.IOException"></exception>
		private void ReadRegexp()
		{
			// should only contain nothing or =, depending on whether
			// starting with /... or /=...
			//assert regexp.length()==0 || regexp.length()==1;
            bool escapedChar = false;
            while (c >= 0 && (c != '/' || escapedChar) && c != '\n')
			{
                if (escapedChar)
                {
                    regexp.Append('\\');
                }
				regexp.Append((char)c);
                escapedChar = false;
				c = reader.Read();
				// completely bypass \r's
				while (c == '\r')
				{
					c = reader.Read();
				}
                //handle escape character
                if (c == '\\')
                {
                    c = reader.Read();
                    escapedChar = true;
                }
			}
			if (c < 0 || c == '\n')
			{
				throw new org.jawk.frontend.AwkParser.LexerException(this, "Unterminated regular expression: "
					 + regexp);
			}
			c = reader.Read();
			// completely bypass \r's
			while (c == '\r')
			{
				c = reader.Read();
			}
		}

		private System.Threading.Thread lexer_thread;

		// LEXER
		private bool AssertOneLexerThread()
		{
			lock (this)
			{
				if (lexer_thread == null)
				{
					lexer_thread = System.Threading.Thread.CurrentThread;
				}
				return lexer_thread == System.Threading.Thread.CurrentThread;
			}
		}

		private static string ToTokenString(int token)
		{
			System.Type c = typeof(org.jawk.frontend.AwkParser);
            System.Reflection.FieldInfo[] fields = c.GetFields();
			try
			{
				foreach (System.Reflection.FieldInfo field in fields)
				{
					if (field.IsStatic
                        && field.FieldType == typeof(int) 
                        && (int)field.GetValue(null) == token)
					{
						return field.Name;
					}
				}
			}
			catch (System.MemberAccessException iac)
			{
				Console.Error.WriteLine(iac.ToString());
				return "[" + token + ": " + iac + "]";
			}
			return "{" + token + "}";
		}

		/// <exception cref="System.IO.IOException"></exception>
		private int Lexer(int expected_token)
		{
			//assert assertOneLexerThread();
			if (token != expected_token)
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting " + expected_token
					 + " " + ToTokenString(expected_token) + ".  Found: " + token + " " + ToTokenString
					(token) + " (" + text + ")");
			}
			return Lexer();
		}

		/// <exception cref="System.IO.IOException"></exception>
		private int Lexer()
		{
			//assert assertOneLexerThread();
			// clear whitespace
			while (c >= 0 && (c == ' ' || c == '\t' || c == '#' || c == '\\'))
			{
				if (c == '\\')
				{
					Read();
					if (c == '\n')
					{
						Read();
					}
					continue;
				}
				if (c == '#')
				{
					// kill comment
					while (c >= 0 && c != '\n')
					{
						Read();
					}
				}
				else
				{
					//// Causes failure when comments are embedded within lines of code...
					////			if (c == '\n')
					////				read();
					Read();
				}
			}
			text.Length = 0;
			if (c < 0)
			{
				return token = _EOF_;
			}
			if (c == ',')
			{
				Read();
				return token = _COMMA_;
			}
			if (c == '(')
			{
				Read();
				return token = _OPEN_PAREN_;
			}
			if (c == ')')
			{
				Read();
				return token = _CLOSE_PAREN_;
			}
			if (c == '{')
			{
				Read();
				return token = _OPEN_BRACE_;
			}
			if (c == '}')
			{
				Read();
				return token = _CLOSE_BRACE_;
			}
			if (c == '[')
			{
				Read();
				return token = _OPEN_BRACKET_;
			}
			if (c == ']')
			{
				Read();
				return token = _CLOSE_BRACKET_;
			}
			if (c == '$')
			{
				Read();
				return token = _DOLLAR_;
			}
			if (c == '~')
			{
				Read();
				return token = _MATCHES_;
			}
			if (c == '?')
			{
				Read();
				return token = _QUESTION_MARK_;
			}
			if (c == ':')
			{
				Read();
				return token = _COLON_;
			}
			if (c == '&')
			{
				Read();
				if (c == '&')
				{
					Read();
					return token = _AND_;
				}
				throw new org.jawk.frontend.AwkParser.LexerException(this, "use && for logical and"
					);
			}
			if (c == '|')
			{
				Read();
				if (c == '|')
				{
					Read();
					return token = _OR_;
				}
				return token = _PIPE_;
			}
			if (c == '=')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _EQ_;
				}
				return token = _EQUALS_;
			}
			if (c == '+')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _PLUS_EQ_;
				}
				else
				{
					if (c == '+')
					{
						Read();
						return token = _INC_;
					}
				}
				return token = _PLUS_;
			}
			if (c == '-')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _MINUS_EQ_;
				}
				else
				{
					if (c == '-')
					{
						Read();
						return token = _DEC_;
					}
				}
				return token = _MINUS_;
			}
			if (c == '*')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _MULT_EQ_;
				}
				return token = _MULT_;
			}
			if (c == '/')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _DIV_EQ_;
				}
				return token = _DIVIDE_;
			}
			if (c == '%')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _MOD_EQ_;
				}
				return token = _MOD_;
			}
			if (c == '^')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _POW_EQ_;
				}
				return token = _POW_;
			}
			if (c == '>')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _GE_;
				}
				else
				{
					if (c == '>')
					{
						Read();
						return token = _APPEND_;
					}
				}
				return token = _GT_;
			}
			if (c == '<')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _LE_;
				}
				return token = _LT_;
			}
			if (c == '!')
			{
				Read();
				if (c == '=')
				{
					Read();
					return token = _NE_;
				}
				else
				{
					if (c == '~')
					{
						Read();
						return token = _NOT_MATCHES_;
					}
				}
				return token = _NOT_;
			}
			if (c == '.')
			{
				// double!
				Read();
				bool hit = false;
				while (c > 0 && char.IsDigit((char)c))
				{
					hit = true;
					Read();
				}
				if (!hit)
				{
					throw new org.jawk.frontend.AwkParser.LexerException(this, "Decimal point encountered with no values on either side."
						);
				}
				return token = _DOUBLE_;
			}
			if (char.IsDigit((char)c))
			{
				// integer or double.
				Read();
				while (c > 0)
				{
					if (c == '.')
					{
						// double!
						Read();
						while (c > 0 && char.IsDigit((char)c))
						{
							Read();
						}
						return token = _DOUBLE_;
					}
					else
					{
                        if (char.IsDigit((char)c))
						{
							// integer or double.
							Read();
						}
						else
						{
							break;
						}
					}
				}
				// integer, only
				return token = _INTEGER_;
			}
			if (Character.IsJavaIdentifierStart((char)c))
			{
				Read();
				while (Character.IsJavaIdentifierPart((char)c))
				{
					Read();
				}
				// check for certain keywords
				// extensions override built-in stuff
                if(extensions.ContainsKey(text.ToString()))
				{
					return token = _EXTENSION_;
				}

                if (KEYWORDS.ContainsKey(text.ToString()))
                {
                    int kw_token = KEYWORDS[text.ToString()];
                    return token = kw_token;
                }

                if (BUILTIN_FUNC_NAMES.ContainsKey(text.ToString()))
                {
                    return token = _BUILTIN_FUNC_NAME_;
                }
			    if (c == '(')
				{
					return token = _FUNC_ID_;
				}
				else
				{
					return token = _ID_;
				}
			}
			if (c == ';')
			{
				Read();
				while (c == ' ' || c == '\t' || c == '\n' || c == '#')
				{
					if (c == '\n')
					{
						break;
					}
					if (c == '#')
					{
						while (c >= 0 && c != '\n')
						{
							Read();
						}
						if (c == '\n')
						{
							Read();
						}
					}
					else
					{
						Read();
					}
				}
				return token = _SEMICOLON_;
			}
			if (c == '\n')
			{
				Read();
				while (c == ' ' || c == '\t' || c == '#' || c == '\n')
				{
					if (c == '#')
					{
						while (c >= 0 && c != '\n')
						{
							Read();
						}
					}
					if (c == '\n')
					{
						Read();
					}
					else
					{
						Read();
					}
				}
				return token = _NEWLINE_;
			}
			if (c == '"')
			{
				// string
				Read();
				@string.Length = 0;
				while (token != _EOF_ && c != '"' && c != '\n')
				{
					if (c == '\\')
					{
						Read();
						if (c == 't')
						{
							@string.Append('\t');
						}
						else
						{
							if (c == 'n')
							{
								@string.Append('\n');
							}
							else
							{
								if (c == 'r')
								{
									@string.Append('\r');
								}
								else
								{
									@string.Append((char)c);
								}
							}
						}
					}
					else
					{
						@string.Append((char)c);
					}
					Read();
				}
				if (token == _EOF_ || c == '\n')
				{
					throw new org.jawk.frontend.AwkParser.LexerException(this, "Unterminated string: "
						 + text);
				}
				Read();
				return token = _STRING_;
			}
			throw new org.jawk.frontend.AwkParser.LexerException(this, "Invalid character (" 
				+ c + "): " + ((char)c));
		}

		// private int lexer() throws IOException
		// SUPPORTING FUNCTIONS/METHODS
		/// <exception cref="System.IO.IOException"></exception>
		private void Terminator()
		{
			// like opt_terminator, except error if no terminator was found
			if (!Opt_terminator())
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting statement terminator.  Got ("
					 + token + "): " + text);
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		private bool Opt_terminator()
		{
			if (Opt_newline())
			{
				return true;
			}
			else
			{
				if (token == _EOF_ || token == _CLOSE_BRACE_)
				{
					return true;
				}
				else
				{
					// do nothing
					if (token == _SEMICOLON_)
					{
						Lexer();
						return true;
					}
					else
					{
						// no terminator consumed
						return false;
					}
				}
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		private bool Opt_newline()
		{
			if (token == _NEWLINE_)
			{
				Lexer();
				return true;
			}
			else
			{
				return false;
			}
		}

		// RECURSIVE DECENT PARSER:
		// SCRIPT : \n [RULE_LIST] EOF
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST SCRIPT()
		{
			org.jawk.frontend.AwkParser.AST rl;
			if (token != _EOF_)
			{
				rl = RULE_LIST();
			}
			else
			{
				rl = null;
			}
			Lexer(_EOF_);
			return rl;
		}

		// RULE_LIST : \n [ ( RULE | FUNCTION terminator ) opt_terminator RULE_LIST ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST RULE_LIST()
		{
			Opt_newline();
			org.jawk.frontend.AwkParser.AST rule_or_function = null;
			if (token == KEYWORDS["function"])
			{
				rule_or_function = FUNCTION();
			}
			else
			{
				if (token != _EOF_)
				{
					rule_or_function = RULE();
				}
				else
				{
					return null;
				}
			}
			Opt_terminator();
			// newline or ; (maybe)
			if (rule_or_function == null)
			{
				return RULE_LIST();
			}
			else
			{
				return new org.jawk.frontend.AwkParser.RuleList_AST(this, rule_or_function, RULE_LIST
					());
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST FUNCTION()
		{
			ExpectKeyword("function");
			string function_name;
			if (token == _FUNC_ID_ || token == _ID_)
			{
				function_name = text.ToString();
				Lexer();
			}
			else
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting function name.  Got ("
					 + token + "): " + text);
			}
			symbol_table.SetFunctionName(function_name);
			Lexer(_OPEN_PAREN_);
			org.jawk.frontend.AwkParser.AST formal_param_list;
			if (token == _CLOSE_PAREN_)
			{
				formal_param_list = null;
			}
			else
			{
				formal_param_list = FORMAL_PARAM_LIST(function_name);
			}
			Lexer(_CLOSE_PAREN_);
			Opt_newline();
			Lexer(_OPEN_BRACE_);
			org.jawk.frontend.AwkParser.AST function_block = STATEMENT_LIST();
			Lexer(_CLOSE_BRACE_);
			symbol_table.ClearFunctionName(function_name);
			return symbol_table.AddFunctionDef(function_name, formal_param_list, function_block
				);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST FORMAL_PARAM_LIST(string func_name
			)
		{
			if (token == _ID_)
			{
				string id = text.ToString();
				int offset = symbol_table.AddFunctionParameter(func_name, id);
				Lexer();
				if (token == _COMMA_)
				{
					Lexer();
					Opt_newline();
					org.jawk.frontend.AwkParser.AST rest = FORMAL_PARAM_LIST(func_name);
					if (rest == null)
					{
						throw new org.jawk.frontend.AwkParser.ParserException(this, "Cannot terminate a formal parameter list with a comma."
							);
					}
					else
					{
						return new org.jawk.frontend.AwkParser.FunctionDefParamList_AST(this, id, offset, 
							rest);
					}
				}
				else
				{
					return new org.jawk.frontend.AwkParser.FunctionDefParamList_AST(this, id, offset, 
						null);
				}
			}
			else
			{
				return null;
			}
		}

		// RULE : [ASSIGNMENT_EXPRESSION] [ { STATEMENT_LIST } ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST RULE()
		{
			org.jawk.frontend.AwkParser.AST opt_expr;
			org.jawk.frontend.AwkParser.AST opt_stmts;
			if (token == KEYWORDS["BEGIN"])
			{
				Lexer();
				opt_expr = symbol_table.AddBEGIN();
			}
			else
			{
				if (token == KEYWORDS["END"])
				{
					Lexer();
					opt_expr = symbol_table.AddEND();
				}
				else
				{
					if (token != _OPEN_BRACE_ && token != _SEMICOLON_ && token != _NEWLINE_ && token 
						!= _EOF_)
					{
						opt_expr = ASSIGNMENT_EXPRESSION(true, true, false);
						// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
						// for /regex/, /regex/
						if (token == _COMMA_)
						{
							Lexer();
							Opt_newline();
							opt_expr = new org.jawk.frontend.AwkParser.RegexpPair_AST(this, opt_expr, ASSIGNMENT_EXPRESSION
								(true, true, false));
						}
					}
					else
					{
						// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
						opt_expr = null;
					}
				}
			}
			if (token == _OPEN_BRACE_)
			{
				Lexer();
				opt_stmts = STATEMENT_LIST();
				Lexer(_CLOSE_BRACE_);
			}
			else
			{
				opt_stmts = null;
			}
			return new org.jawk.frontend.AwkParser.Rule_AST(this, opt_expr, opt_stmts);
		}

		// STATEMENT_LIST : [ STATEMENT_BLOCK|STATEMENT STATEMENT_LIST ]
		/// <exception cref="System.IO.IOException"></exception>
		private org.jawk.frontend.AwkParser.AST STATEMENT_LIST()
		{
			// statement lists can only live within curly brackets (braces)
			Opt_newline();
			if (token == _CLOSE_BRACE_ || token == _EOF_)
			{
				return null;
			}
			org.jawk.frontend.AwkParser.AST stmt;
			if (token == _OPEN_BRACE_)
			{
				Lexer();
				stmt = STATEMENT_LIST();
				Lexer(_CLOSE_BRACE_);
			}
			else
			{
				if (token == _SEMICOLON_)
				{
					// an empty statement (;)
					// do not polute the syntax tree with nulls in this case
					// just return the next statement (recursively)
					Lexer();
					return STATEMENT_LIST();
				}
				else
				{
					stmt = STATEMENT();
				}
			}
			org.jawk.frontend.AwkParser.AST rest = STATEMENT_LIST();
			if (rest == null)
			{
				return stmt;
			}
			else
			{
				if (stmt == null)
				{
					return rest;
				}
				else
				{
					return new org.jawk.frontend.AwkParser.STATEMENTLIST_AST(this, stmt, rest);
				}
			}
		}

		// EXPRESSION_LIST : ASSIGNMENT_EXPRESSION [, EXPRESSION_LIST]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST EXPRESSION_LIST(bool allow_comparators
			, bool allow_in_keyword)
		{
			org.jawk.frontend.AwkParser.AST expr = ASSIGNMENT_EXPRESSION(allow_comparators, allow_in_keyword
				, false);
			// do NOT allow multidim indices expressions
			if (token == _COMMA_)
			{
				Lexer();
				Opt_newline();
				return new org.jawk.frontend.AwkParser.FunctionCallParamList_AST(this, expr, EXPRESSION_LIST
					(allow_comparators, allow_in_keyword));
			}
			else
			{
				return new org.jawk.frontend.AwkParser.FunctionCallParamList_AST(this, expr, null
					);
			}
		}

		// ASSIGNMENT_EXPRESSION = COMMA_EXPRESSION [ (=,+=,-=,*=) ASSIGNMENT_EXPRESSION ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST ASSIGNMENT_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST comma_expression = COMMA_EXPRESSION(allow_comparators
				, allow_in_keyword, allow_multidim_indices);
			int op = 0;
			string txt = null;
			org.jawk.frontend.AwkParser.AST assignment_expression = null;
			if (token == _EQUALS_ || token == _PLUS_EQ_ || token == _MINUS_EQ_ || token == _MULT_EQ_
				 || token == _DIV_EQ_ || token == _MOD_EQ_ || token == _POW_EQ_)
			{
				op = token;
				txt = text.ToString();
				Lexer();
				assignment_expression = ASSIGNMENT_EXPRESSION(allow_comparators, allow_in_keyword
					, allow_multidim_indices);
				return new org.jawk.frontend.AwkParser.AssignmentExpression_AST(this, comma_expression
					, op, txt, assignment_expression);
			}
			return comma_expression;
		}

		// COMMA_EXPRESSION = CONCAT_EXPRESSION [, COMMA_EXPRESSION] !!!ONLY IF!!! allow_multidim_indices is true
		// allow_multidim_indices is set to true when we need (1,2,3,4) expressions to collapse into an array index expression
		// (converts 1,2,3,4 to 1 SUBSEP 2 SUBSEP 3 SUBSEP 4) after an open parenthesis (grouping) expression starter
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST COMMA_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST concat_expression = CONCAT_EXPRESSION(allow_comparators
				, allow_in_keyword, allow_multidim_indices);
			if (allow_multidim_indices && token == _COMMA_)
			{
				// consume the comma
				Lexer();
				Opt_newline();
				org.jawk.frontend.AwkParser.AST rest = COMMA_EXPRESSION(allow_comparators, allow_in_keyword
					, allow_multidim_indices);
				if (rest is org.jawk.frontend.AwkParser.ArrayIndex_AST)
				{
					return new org.jawk.frontend.AwkParser.ArrayIndex_AST(this, concat_expression, rest
						);
				}
				else
				{
					return new org.jawk.frontend.AwkParser.ArrayIndex_AST(this, concat_expression, new 
						org.jawk.frontend.AwkParser.ArrayIndex_AST(this, rest, null));
				}
			}
			else
			{
				return concat_expression;
			}
		}

		// CONCAT_EXPRESSION = LE_1 [ CONCAT_EXPRESSION ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST CONCAT_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST te = TERTIARY_EXPRESSION(allow_comparators, allow_in_keyword
				, allow_multidim_indices);
			if (token == _INTEGER_ || token == _DOUBLE_ || token == _OPEN_PAREN_ || token == 
				_FUNC_ID_ || token == _INC_ || token == _DEC_ || token == _ID_ || token == _STRING_
				 || token == _DOLLAR_ || token == _BUILTIN_FUNC_NAME_ || token == _EXTENSION_)
			{
				// allow concatination here only when certain tokens follow
				return new org.jawk.frontend.AwkParser.ConcatExpression_AST(this, te, CONCAT_EXPRESSION
					(allow_comparators, allow_in_keyword, allow_multidim_indices));
			}
			else
			{
				if (additional_type_functions && (token == KEYWORDS["_INTEGER"] || token == KEYWORDS
					["_DOUBLE"] || token == KEYWORDS["_STRING"]))
				{
					// allow concatination here only when certain tokens follow
					return new org.jawk.frontend.AwkParser.ConcatExpression_AST(this, te, CONCAT_EXPRESSION
						(allow_comparators, allow_in_keyword, allow_multidim_indices));
				}
				else
				{
					return te;
				}
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST TERTIARY_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST le1 = LE1_EXPRESSION(allow_comparators, allow_in_keyword
				, allow_multidim_indices);
			if (token == _QUESTION_MARK_)
			{
				Lexer();
				org.jawk.frontend.AwkParser.AST true_block = TERTIARY_EXPRESSION(allow_comparators
					, allow_in_keyword, allow_multidim_indices);
				Lexer(_COLON_);
				org.jawk.frontend.AwkParser.AST false_block = TERTIARY_EXPRESSION(allow_comparators
					, allow_in_keyword, allow_multidim_indices);
				return new org.jawk.frontend.AwkParser.TertiaryExpression_AST(this, le1, true_block
					, false_block);
			}
			else
			{
				return le1;
			}
		}

		// LE_1 = LE_2 [ || LE_1 ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST LE1_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST le2 = LE2_EXPRESSION(allow_comparators, allow_in_keyword
				, allow_multidim_indices);
			int op = 0;
			string txt = null;
			org.jawk.frontend.AwkParser.AST le1 = null;
			if (token == _OR_)
			{
				op = token;
				txt = text.ToString();
				Lexer();
				le1 = LE1_EXPRESSION(allow_comparators, allow_in_keyword, allow_multidim_indices);
				return new org.jawk.frontend.AwkParser.LogicalExpression_AST(this, le2, op, txt, 
					le1);
			}
			return le2;
		}

		// LE_2 = COMPARISON_EXPRESSION [ && LE_2 ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST LE2_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST comparison_expression = COMPARISON_EXPRESSION(allow_comparators
				, allow_in_keyword, allow_multidim_indices);
			int op = 0;
			string txt = null;
			org.jawk.frontend.AwkParser.AST le2 = null;
			if (token == _AND_)
			{
				op = token;
				txt = text.ToString();
				Lexer();
				le2 = LE2_EXPRESSION(allow_comparators, allow_in_keyword, allow_multidim_indices);
				return new org.jawk.frontend.AwkParser.LogicalExpression_AST(this, comparison_expression
					, op, txt, le2);
			}
			return comparison_expression;
		}

		// COMPARISON_EXPRESSION = EXPRESSION [ (==,>,>=,<,<=,!=,~,!~) COMPARISON_EXPRESSION ]
		// allow_comparators is set false when within a print/printf statement;
		// all other times it is set true
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST COMPARISON_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST expression = EXPRESSION(allow_comparators, allow_in_keyword
				, allow_multidim_indices);
			int op = 0;
			string txt = null;
			org.jawk.frontend.AwkParser.AST comparison_expression = null;
			if (allow_comparators)
			{
				if (token == _EQ_ || token == _GT_ || token == _GE_ || token == _LT_ || token == 
					_LE_ || token == _NE_ || token == _MATCHES_ || token == _NOT_MATCHES_)
				{
					op = token;
					txt = text.ToString();
					Lexer();
					comparison_expression = COMPARISON_EXPRESSION(allow_comparators, allow_in_keyword
						, allow_multidim_indices);
					return new org.jawk.frontend.AwkParser.ComparisonExpression_AST(this, expression, 
						op, txt, comparison_expression);
				}
			}
			return expression;
		}

		// EXPRESSION : TERM [ (+|-|,) EXPRESSION ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST term = TERM(allow_comparators, allow_in_keyword, 
				allow_multidim_indices);
			int op = 0;
			string txt = null;
			org.jawk.frontend.AwkParser.AST expression = null;
			if (token == _PLUS_ || token == _MINUS_)
			{
				//|| token == _COMMA_
				op = token;
				txt = text.ToString();
				Lexer();
				expression = EXPRESSION(allow_comparators, allow_in_keyword, allow_multidim_indices
					);
				org.jawk.frontend.AwkParser.BinaryExpression_AST retval = new org.jawk.frontend.AwkParser.BinaryExpression_AST
					(this, term, op, txt, expression);
				if (expression is org.jawk.frontend.AwkParser.BinaryExpression_AST)
				{
					org.jawk.frontend.AwkParser.BinaryExpression_AST be2 = (org.jawk.frontend.AwkParser.BinaryExpression_AST
						)expression;
					if (be2.op == _PLUS_ || be2.op == _MINUS_)
					{
						// convert right-associativity to left-assiciativity
						return Rearrange(retval, be2);
					}
				}
				return retval;
			}
			return term;
		}

		// Before:
		//
		//     |
		//     |
		//    b1
		//   /  \
		//  /    \
		// a     b2
		//      /  \
		//     /    \
		//    b      c
		//
		// After:
		//
		//         |
		//         |
		//        b2
		//       /  \
		//      /    \
		//    b1      c
		//   /  \
		//  /    \
		// a      b
		private static org.jawk.frontend.AwkParser.BinaryExpression_AST Rearrange(org.jawk.frontend.AwkParser.BinaryExpression_AST
			 b1, org.jawk.frontend.AwkParser.BinaryExpression_AST b2)
		{
			//assert b1.ast2 == b2;
			org.jawk.frontend.AwkParser.AST a = b1.ast1;
			org.jawk.frontend.AwkParser.AST b = b2.ast1;
			org.jawk.frontend.AwkParser.AST c = b2.ast2;
			b1.ast1 = a;
			b1.ast2 = b;
			b2.ast2 = c;
			b2.ast1 = b1;
			return b2;
		}

		// TERM : FACTOR_FOR_GETLINE [ (*|/|%|^) TERM ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST TERM(bool allow_comparators, bool
			 allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST factor_for_getline = FACTOR_FOR_GETLINE(allow_comparators
				, allow_in_keyword, allow_multidim_indices);
			int op = 0;
			string txt = null;
			org.jawk.frontend.AwkParser.AST term = null;
			if (token == _MULT_ || token == _DIVIDE_ || token == _MOD_ || token == _POW_)
			{
				op = token;
				txt = text.ToString();
				Lexer();
				term = TERM(allow_comparators, allow_in_keyword, allow_multidim_indices);
				org.jawk.frontend.AwkParser.BinaryExpression_AST retval = new org.jawk.frontend.AwkParser.BinaryExpression_AST
					(this, factor_for_getline, op, txt, term);
				if (term is org.jawk.frontend.AwkParser.BinaryExpression_AST)
				{
					org.jawk.frontend.AwkParser.BinaryExpression_AST be2 = (org.jawk.frontend.AwkParser.BinaryExpression_AST
						)term;
					if (be2.op == _MULT_ || be2.op == _DIVIDE_ || be2.op == _MOD_ || be2.op == _POW_)
					{
						// convert right-associativity to left-assiciativity
						return Rearrange(retval, be2);
					}
				}
				return retval;
			}
			return factor_for_getline;
		}

		// FACTOR_FOR_GETLINE : FACTOR_FOR_IN [ | getline_expr ]
		// allow_comparators is set false when within a print/printf statement;
		// all other times it is set true
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST FACTOR_FOR_GETLINE(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			org.jawk.frontend.AwkParser.AST factor_for_in = FACTOR_FOR_IN(allow_comparators, 
				allow_in_keyword, allow_multidim_indices);
			if (allow_comparators && token == _PIPE_)
			{
				Lexer();
				return GETLINE_EXPRESSION(factor_for_in, allow_comparators, allow_in_keyword);
			}
			return factor_for_in;
		}

		// allow_in_keyword is set false while parsing the first expression within
		// a for() statement (because it could be "for (key in arr)", and this
		// production will consume and the for statement will never have a chance
		// of processing it
		// all other times, it is true
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST FACTOR_FOR_IN(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			// true = allow post_inc/dec operators
			org.jawk.frontend.AwkParser.AST factor_for_incdec = FACTOR_FOR_INCDEC(allow_comparators
				, allow_in_keyword, allow_multidim_indices);
			if (allow_in_keyword && token == KEYWORDS["in"])
			{
				Lexer();
				return new org.jawk.frontend.AwkParser.InExpression_AST(this, factor_for_incdec, 
					FACTOR_FOR_IN(allow_comparators, allow_in_keyword, allow_multidim_indices));
			}
			return factor_for_incdec;
		}

		// according to the spec, pre/post inc can occur
		// only on lvalues, which are NAMES (IDs), array,
		// or field references
		private bool IsLvalue(org.jawk.frontend.AwkParser.AST ast)
		{
			return false || (ast is org.jawk.frontend.AwkParser.ID_AST) || (ast is org.jawk.frontend.AwkParser.ArrayReference_AST
				) || (ast is org.jawk.frontend.AwkParser.DollarExpression_AST);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST FACTOR_FOR_INCDEC(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			bool pre_inc = false;
			bool pre_dec = false;
			bool post_inc = false;
			bool post_dec = false;
			if (token == _INC_)
			{
				pre_inc = true;
				Lexer();
			}
			else
			{
				if (token == _DEC_)
				{
					pre_dec = true;
					Lexer();
				}
			}
			org.jawk.frontend.AwkParser.AST factor_ast = FACTOR(allow_comparators, allow_in_keyword
				, allow_multidim_indices);
			if (pre_inc || pre_dec)
			{
				if (!IsLvalue(factor_ast))
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "Cannot pre inc/dec a non-lvalue"
						);
				}
			}
			// only do post ops if:
			// - factor_ast is an lvalue
			// - pre ops were not encountered
			if (IsLvalue(factor_ast) && !pre_inc && !pre_dec)
			{
				if (token == _INC_)
				{
					post_inc = true;
					Lexer();
				}
				else
				{
					if (token == _DEC_)
					{
						post_dec = true;
						Lexer();
					}
				}
			}
			if ((pre_inc || pre_dec) && (post_inc || post_dec))
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Cannot do pre inc/dec AND post inc/dec."
					);
			}
			if (pre_inc)
			{
				return new org.jawk.frontend.AwkParser.PreInc_AST(this, factor_ast);
			}
			else
			{
				if (pre_dec)
				{
					return new org.jawk.frontend.AwkParser.PreDec_AST(this, factor_ast);
				}
				else
				{
					if (post_inc)
					{
						return new org.jawk.frontend.AwkParser.PostInc_AST(this, factor_ast);
					}
					else
					{
						if (post_dec)
						{
							return new org.jawk.frontend.AwkParser.PostDec_AST(this, factor_ast);
						}
						else
						{
							return factor_ast;
						}
					}
				}
			}
		}

		// FACTOR : '(' ASSIGNMENT_EXPRESSION ')' | ! FACTOR | $ FACTOR | - FACTOR | _INTEGER_ | _DOUBLE_ | _STRING_ | GETLINE [ID-or-array-or-$val] | /[=].../ | [++|--] SYMBOL [++|--]
		//AST FACTOR(boolean allow_comparators, boolean allow_in_keyword, boolean allow_post_incdec_operators)
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST FACTOR(bool allow_comparators, bool
			 allow_in_keyword, bool allow_multidim_indices)
		{
			if (token == _OPEN_PAREN_)
			{
				Lexer();
				// true = allow multi-dimensional array indices (i.e., commas for 1,2,3,4)
				org.jawk.frontend.AwkParser.AST assignment_expression = ASSIGNMENT_EXPRESSION(allow_comparators
					, allow_in_keyword, true);
				if (allow_multidim_indices && (assignment_expression is org.jawk.frontend.AwkParser.ArrayIndex_AST
					))
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "Cannot nest multi-dimensional array index expressions."
						);
				}
				Lexer(_CLOSE_PAREN_);
				return assignment_expression;
			}
			else
			{
				if (token == _NOT_)
				{
					Lexer();
					return new org.jawk.frontend.AwkParser.NotExpression_AST(this, FACTOR(allow_comparators
						, allow_in_keyword, allow_multidim_indices));
				}
				else
				{
					if (token == _MINUS_)
					{
						Lexer();
						return new org.jawk.frontend.AwkParser.NegativeExpression_AST(this, FACTOR(allow_comparators
							, allow_in_keyword, allow_multidim_indices));
					}
					else
					{
						if (token == _INTEGER_)
						{
							org.jawk.frontend.AwkParser.AST integer = symbol_table.AddINTEGER(text.ToString()
								);
							Lexer();
							return integer;
						}
						else
						{
							if (token == _DOUBLE_)
							{
								org.jawk.frontend.AwkParser.AST dbl = symbol_table.AddDOUBLE(text.ToString());
								Lexer();
								return dbl;
							}
							else
							{
								if (token == _STRING_)
								{
									org.jawk.frontend.AwkParser.AST str = symbol_table.AddSTRING(@string.ToString());
									Lexer();
									return str;
								}
								else
								{
									if (token == KEYWORDS["getline"])
									{
										return GETLINE_EXPRESSION(null, allow_comparators, allow_in_keyword);
									}
									else
									{
										if (token == _DIVIDE_ || token == _DIV_EQ_)
										{
											// /.../ or /=.../
											regexp.Length = 0;
											if (token == _DIV_EQ_)
											{
												regexp.Append('=');
											}
											ReadRegexp();
											org.jawk.frontend.AwkParser.AST regexp_ast = symbol_table.AddREGEXP(regexp.ToString
												());
											Lexer();
											return regexp_ast;
										}
										else
										{
											if (additional_type_functions && token == KEYWORDS["_INTEGER"])
											{
												return INTEGER_EXPRESSION(allow_comparators, allow_in_keyword, allow_multidim_indices
													);
											}
											else
											{
												if (additional_type_functions && token == KEYWORDS["_DOUBLE"])
												{
													return DOUBLE_EXPRESSION(allow_comparators, allow_in_keyword, allow_multidim_indices
														);
												}
												else
												{
													if (additional_type_functions && token == KEYWORDS["_STRING"])
													{
														return STRING_EXPRESSION(allow_comparators, allow_in_keyword, allow_multidim_indices
															);
													}
													else
													{
														org.jawk.frontend.AwkParser.AST target_ast;
														if (token == _DOLLAR_)
														{
															Lexer();
															target_ast = new org.jawk.frontend.AwkParser.DollarExpression_AST(this, FACTOR(allow_comparators
																, allow_in_keyword, allow_multidim_indices));
														}
														else
														{
															target_ast = SYMBOL(allow_comparators, allow_in_keyword);
														}
														return target_ast;
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST INTEGER_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			bool parens = (c == '(');
			ExpectKeyword("_INTEGER");
			if (token == _SEMICOLON_ || token == _NEWLINE_ || token == _CLOSE_BRACE_)
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "expression expected"
					);
			}
			else
			{
				// do NOT allow for a blank param list: "()" using the parens boolean below
				// otherwise, the parser will complain because assignment_expression cannot be ()
				if (parens)
				{
					Lexer(_OPEN_PAREN_);
				}
				org.jawk.frontend.AwkParser.AST int_expr_ast;
				if (token == _CLOSE_PAREN_)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "expression expected"
						);
				}
				else
				{
					int_expr_ast = new org.jawk.frontend.AwkParser.IntegerExpression_AST(this, ASSIGNMENT_EXPRESSION
						(allow_comparators, allow_in_keyword, allow_multidim_indices));
				}
				if (parens)
				{
					Lexer(_CLOSE_PAREN_);
				}
				return int_expr_ast;
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST DOUBLE_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			bool parens = (c == '(');
			ExpectKeyword("_DOUBLE");
			if (token == _SEMICOLON_ || token == _NEWLINE_ || token == _CLOSE_BRACE_)
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "expression expected"
					);
			}
			else
			{
				// do NOT allow for a blank param list: "()" using the parens boolean below
				// otherwise, the parser will complain because assignment_expression cannot be ()
				if (parens)
				{
					Lexer(_OPEN_PAREN_);
				}
				org.jawk.frontend.AwkParser.AST double_expr_ast;
				if (token == _CLOSE_PAREN_)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "expression expected"
						);
				}
				else
				{
					double_expr_ast = new org.jawk.frontend.AwkParser.DoubleExpression_AST(this, ASSIGNMENT_EXPRESSION
						(allow_comparators, allow_in_keyword, allow_multidim_indices));
				}
				if (parens)
				{
					Lexer(_CLOSE_PAREN_);
				}
				return double_expr_ast;
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST STRING_EXPRESSION(bool allow_comparators
			, bool allow_in_keyword, bool allow_multidim_indices)
		{
			bool parens = (c == '(');
			ExpectKeyword("_STRING");
			if (token == _SEMICOLON_ || token == _NEWLINE_ || token == _CLOSE_BRACE_)
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "expression expected"
					);
			}
			else
			{
				// do NOT allow for a blank param list: "()" using the parens boolean below
				// otherwise, the parser will complain because assignment_expression cannot be ()
				if (parens)
				{
					Lexer(_OPEN_PAREN_);
				}
				org.jawk.frontend.AwkParser.AST string_expr_ast;
				if (token == _CLOSE_PAREN_)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "expression expected"
						);
				}
				else
				{
					string_expr_ast = new org.jawk.frontend.AwkParser.StringExpression_AST(this, ASSIGNMENT_EXPRESSION
						(allow_comparators, allow_in_keyword, allow_multidim_indices));
				}
				if (parens)
				{
					Lexer(_CLOSE_PAREN_);
				}
				return string_expr_ast;
			}
		}

		// SYMBOL : _ID_ [ '(' params ')' | '[' ASSIGNMENT_EXPRESSION ']' ]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST SYMBOL(bool allow_comparators, bool
			 allow_in_keyword)
		{
			if (token != _ID_ && token != _FUNC_ID_ && token != _BUILTIN_FUNC_NAME_ && token 
				!= _EXTENSION_)
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting an ID.  Got ("
					 + token + "): " + text);
			}
			int id_token = token;
			string id = text.ToString();
			bool parens = (c == '(');
			Lexer();
			if (id_token == _EXTENSION_)
			{
				string extension_keyword = id;
				org.jawk.ext.IJawkExtension extension = extensions[extension_keyword];
				org.jawk.frontend.AwkParser.AST @params;
				//if (extension.requiresParens() || parens)
				if (parens)
				{
					Lexer();
					if (token == _CLOSE_PAREN_)
					{
						@params = null;
					}
					else
					{
						//?//params = EXPRESSION_LIST(false,true);	// NO comparators allowed, allow in expression
						@params = EXPRESSION_LIST(allow_comparators, allow_in_keyword);
					}
					// NO comparators allowed, allow in expression
					Lexer(_CLOSE_PAREN_);
				}
				else
				{
					@params = null;
				}
				return new org.jawk.frontend.AwkParser.Extension_AST(this, extension_keyword, @params
					);
			}
			else
			{
				if (id_token == _FUNC_ID_ || id_token == _BUILTIN_FUNC_NAME_)
				{
					org.jawk.frontend.AwkParser.AST @params;
					// length can take on the special form of no parens
					if (id.Equals("length"))
					{
						if (token == _OPEN_PAREN_)
						{
							Lexer();
							if (token == _CLOSE_PAREN_)
							{
								@params = null;
							}
							else
							{
								@params = EXPRESSION_LIST(allow_comparators, allow_in_keyword);
							}
							Lexer(_CLOSE_PAREN_);
						}
						else
						{
							@params = null;
						}
					}
					else
					{
						Lexer(_OPEN_PAREN_);
						if (token == _CLOSE_PAREN_)
						{
							@params = null;
						}
						else
						{
							@params = EXPRESSION_LIST(allow_comparators, allow_in_keyword);
						}
						Lexer(_CLOSE_PAREN_);
					}
					if (id_token == _BUILTIN_FUNC_NAME_)
					{
						return new org.jawk.frontend.AwkParser.BuiltinFunctionCall_AST(this, id, @params);
					}
					else
					{
						return symbol_table.AddFunctionCall(id, @params);
					}
				}
			}
			if (token == _OPEN_BRACKET_)
			{
				Lexer();
				org.jawk.frontend.AwkParser.AST idx_ast = ARRAY_INDEX(allow_comparators, allow_in_keyword
					);
				Lexer(_CLOSE_BRACKET_);
				if (token == _OPEN_BRACKET_)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "Use [a,b,c,...] instead of [a][b][c]... for multi-dimensional arrays."
						);
				}
				return symbol_table.AddArrayReference(id, idx_ast);
			}
			return symbol_table.AddID(id);
		}

		// ARRAY_INDEX : ASSIGNMENT_EXPRESSION [, ARRAY_INDEX]
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST ARRAY_INDEX(bool allow_comparators
			, bool allow_in_keyword)
		{
			org.jawk.frontend.AwkParser.AST expr_ast = ASSIGNMENT_EXPRESSION(allow_comparators
				, allow_in_keyword, false);
			if (token == _COMMA_)
			{
				Opt_newline();
				Lexer();
				return new org.jawk.frontend.AwkParser.ArrayIndex_AST(this, expr_ast, ARRAY_INDEX
					(allow_comparators, allow_in_keyword));
			}
			else
			{
				return new org.jawk.frontend.AwkParser.ArrayIndex_AST(this, expr_ast, null);
			}
		}

		// STATEMENT :
		// 	IF_STATEMENT
		// 	| WHILE_STATEMENT
		// 	| FOR_STATEMENT
		// 	| DO_STATEMENT
		// 	| RETURN_STATEMENT
		// 	| ASSIGNMENT_EXPRESSION
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST STATEMENT()
		{
			if (token == _OPEN_BRACE_)
			{
				Lexer();
				org.jawk.frontend.AwkParser.AST lst = STATEMENT_LIST();
				Lexer(_CLOSE_BRACE_);
				return lst;
			}
			org.jawk.frontend.AwkParser.AST stmt;
			if (token == KEYWORDS["if"])
			{
				stmt = IF_STATEMENT();
			}
			else
			{
				if (token == KEYWORDS["while"])
				{
					stmt = WHILE_STATEMENT();
				}
				else
				{
					if (token == KEYWORDS["for"])
					{
						stmt = FOR_STATEMENT();
					}
					else
					{
						if (token == KEYWORDS["do"])
						{
							stmt = DO_STATEMENT();
						}
						else
						{
							if (token == KEYWORDS["return"])
							{
								stmt = RETURN_STATEMENT();
							}
							else
							{
								if (token == KEYWORDS["exit"])
								{
									stmt = EXIT_STATEMENT();
								}
								else
								{
									if (token == KEYWORDS["delete"])
									{
										stmt = DELETE_STATEMENT();
									}
									else
									{
										if (token == KEYWORDS["print"])
										{
											stmt = PRINT_STATEMENT();
										}
										else
										{
											if (token == KEYWORDS["printf"])
											{
												stmt = PRINTF_STATEMENT();
											}
											else
											{
												if (token == KEYWORDS["next"])
												{
													stmt = NEXT_STATEMENT();
												}
												else
												{
													if (token == KEYWORDS["continue"])
													{
														stmt = CONTINUE_STATEMENT();
													}
													else
													{
														if (token == KEYWORDS["break"])
														{
															stmt = BREAK_STATEMENT();
														}
														else
														{
															if (additional_functions && token == KEYWORDS["_sleep"])
															{
																stmt = SLEEP_STATEMENT();
															}
															else
															{
																if (additional_functions && token == KEYWORDS["_dump"])
																{
																	stmt = DUMP_STATEMENT();
																}
																else
																{
																	stmt = EXPRESSION_STATEMENT(true, false);
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
						// allow in keyword, do NOT allow NonStatement_ASTs
						Terminator();
						return stmt;
					}
				}
			}
			// NO TERMINATOR FOR IF, WHILE, AND FOR
			// (leave it for absorption by the callee)
			return stmt;
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST EXPRESSION_STATEMENT(bool allow_in_keyword
			, bool allow_non_statement_asts)
		{
			// true = allow comparators
			// false = do NOT allow multi-dimensional array indices
			//return new ExpressionStatement_AST(ASSIGNMENT_EXPRESSION(true, allow_in_keyword, false));
			org.jawk.frontend.AwkParser.AST expr_ast = ASSIGNMENT_EXPRESSION(true, allow_in_keyword
				, false);
			if (!allow_non_statement_asts && expr_ast is org.jawk.frontend.AwkParser.INonStatement_AST)
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Not a valid statement."
					);
			}
			return new org.jawk.frontend.AwkParser.ExpressionStatement_AST(this, expr_ast);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST IF_STATEMENT()
		{
			ExpectKeyword("if");
			Lexer(_OPEN_PAREN_);
			org.jawk.frontend.AwkParser.AST expr = ASSIGNMENT_EXPRESSION(true, true, false);
			// allow comparators, allow in keyword, do NOT allow multidim indices expressions
			Lexer(_CLOSE_PAREN_);
			//// Was:
			//// AST b1 = BLOCK_OR_STMT();
			//// But it didn't handle
			//// if ; else ...
			//// properly
			Opt_newline();
			org.jawk.frontend.AwkParser.AST b1;
			if (token == _SEMICOLON_)
			{
				Lexer();
				// consume the newline after the semicolon
				Opt_newline();
				b1 = null;
			}
			else
			{
				b1 = BLOCK_OR_STMT();
			}
			// The OPT_NEWLINE() above causes issues with the following form:
			// if (...) {
			// }
			// else { ... }
			// The \n before the else disassociates subsequent statements
			// if an "else" does not immediately follow.
			// To accommodate, the if_statement will continue to manage
			// statements, causing the original OPT_STATEMENT_LIST to relinquish
			// processing statements to this OPT_STATEMENT_LIST.
			Opt_newline();
			if (token == KEYWORDS["else"])
			{
				Lexer();
				Opt_newline();
				org.jawk.frontend.AwkParser.AST b2 = BLOCK_OR_STMT();
				return new org.jawk.frontend.AwkParser.IfStatement_AST(this, expr, b1, b2);
			}
			else
			{
				org.jawk.frontend.AwkParser.AST if_ast = new org.jawk.frontend.AwkParser.IfStatement_AST
					(this, expr, b1, null);
				return if_ast;
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST BREAK_STATEMENT()
		{
			ExpectKeyword("break");
			return new org.jawk.frontend.AwkParser.BreakStatement_AST(this);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST BLOCK_OR_STMT()
		{
			// default case, does NOT consume (require) a terminator
			return BLOCK_OR_STMT(false);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST BLOCK_OR_STMT(bool require_terminator
			)
		{
			Opt_newline();
			org.jawk.frontend.AwkParser.AST block;
			// HIJACK BRACES HERE SINCE WE MAY NOT HAVE A TERMINATOR AFTER THE CLOSING BRACE
			if (token == _OPEN_BRACE_)
			{
				Lexer();
				block = STATEMENT_LIST();
				Lexer(_CLOSE_BRACE_);
				return block;
			}
			else
			{
				if (token == _SEMICOLON_)
				{
					block = null;
				}
				else
				{
					block = STATEMENT();
				}
			}
			// NO TERMINATOR HERE!
			if (require_terminator)
			{
				Terminator();
			}
			return block;
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST WHILE_STATEMENT()
		{
			ExpectKeyword("while");
			Lexer(_OPEN_PAREN_);
			org.jawk.frontend.AwkParser.AST expr = ASSIGNMENT_EXPRESSION(true, true, false);
			// allow comparators, allow IN keyword, do NOT allow multidim indices expressions
			Lexer(_CLOSE_PAREN_);
			org.jawk.frontend.AwkParser.AST block = BLOCK_OR_STMT();
			return new org.jawk.frontend.AwkParser.WhileStatement_AST(this, expr, block);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST FOR_STATEMENT()
		{
			ExpectKeyword("for");
			org.jawk.frontend.AwkParser.AST expr1 = null;
			org.jawk.frontend.AwkParser.AST expr2 = null;
			org.jawk.frontend.AwkParser.AST expr3 = null;
			Lexer(_OPEN_PAREN_);
			expr1 = OPT_SIMPLE_STATEMENT(false);
			// false = "no in keyword allowed"
			// branch here if we expect a for(... in ...) statement
			if (token == KEYWORDS["in"])
			{
				if (expr1.ast1 == null || expr1.ast2 != null)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "Invalid expression prior to 'in' statement.  Got : "
						 + expr1);
				}
				expr1 = expr1.ast1;
				// analyze expr1 to make sure it's a singleton ID_AST
				if (expr1 == null || !(expr1 is org.jawk.frontend.AwkParser.ID_AST))
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting an ID for 'in' statement.  Got : "
						 + expr1);
				}
				// in
				Lexer();
				// id
				if (token != _ID_)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting an ARRAY ID for 'in' statement.  Got ("
						 + token + "): " + text);
				}
				string arr_id = text.ToString();
				// not an indexed array reference!
				org.jawk.frontend.AwkParser.AST array_id_ast = symbol_table.AddArrayID(arr_id);
				Lexer();
				// close paren ...
				Lexer(_CLOSE_PAREN_);
				org.jawk.frontend.AwkParser.AST lBlock = BLOCK_OR_STMT();
				return new org.jawk.frontend.AwkParser.ForInStatement_AST(this, expr1, array_id_ast , lBlock);
			}
			if (token == _SEMICOLON_)
			{
				Lexer();
			}
			else
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting ;.  Got ("
					 + token + "): " + text);
			}
			if (token != _SEMICOLON_)
			{
				expr2 = ASSIGNMENT_EXPRESSION(true, true, false);
			}
			// allow comparators, allow IN keyword, do NOT allow multidim indices expressions
			if (token == _SEMICOLON_)
			{
				Lexer();
			}
			else
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting ;.  Got ("
					 + token + "): " + text);
			}
			if (token != _CLOSE_PAREN_)
			{
				expr3 = OPT_SIMPLE_STATEMENT(true);
			}
			// true = "allow the in keyword"
			Lexer(_CLOSE_PAREN_);
			org.jawk.frontend.AwkParser.AST block = BLOCK_OR_STMT();
			return new org.jawk.frontend.AwkParser.ForStatement_AST(this, expr1, expr2, expr3
				, block);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST OPT_SIMPLE_STATEMENT(bool allow_in_keyword
			)
		{
			if (token == _SEMICOLON_)
			{
				return null;
			}
			else
			{
				if (token == KEYWORDS["delete"])
				{
					return DELETE_STATEMENT();
				}
				else
				{
					if (token == KEYWORDS["print"])
					{
						return PRINT_STATEMENT();
					}
					else
					{
						if (token == KEYWORDS["printf"])
						{
							return PRINTF_STATEMENT();
						}
						else
						{
							return EXPRESSION_STATEMENT(allow_in_keyword, true);
						}
					}
				}
			}
		}

		// allow NonStatement_ASTs
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST DELETE_STATEMENT()
		{
			bool parens = (c == '(');
			ExpectKeyword("delete");
			if (parens)
			{
				//assert token == _OPEN_PAREN_;
				Lexer();
			}
			org.jawk.frontend.AwkParser.AST symbol_ast = SYMBOL(true, true);
			// allow comparators
			if (parens)
			{
				Lexer(_CLOSE_PAREN_);
			}
			return new org.jawk.frontend.AwkParser.DeleteStatement_AST(this, symbol_ast);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST PRINT_STATEMENT()
		{
			bool parens = (c == '(');
			ExpectKeyword("print");
			org.jawk.frontend.AwkParser.AST func_params;
			int output_token;
			org.jawk.frontend.AwkParser.AST output_expr;
			if (parens)
			{
				Lexer();
				if (token == _CLOSE_PAREN_)
				{
					func_params = null;
				}
				else
				{
					func_params = EXPRESSION_LIST(false, true);
				}
				// NO comparators allowed, allow in expression
				Lexer(_CLOSE_PAREN_);
			}
			else
			{
				if (token == _NEWLINE_ || token == _SEMICOLON_ || token == _CLOSE_BRACE_ || token
					 == _CLOSE_PAREN_ || (token == _GT_ || token == _APPEND_ || token == _PIPE_))
				{
					func_params = null;
				}
				else
				{
					func_params = EXPRESSION_LIST(false, true);
				}
			}
			// NO comparators allowed, allow in expression
			if (token == _GT_ || token == _APPEND_ || token == _PIPE_)
			{
				output_token = token;
				Lexer();
				output_expr = ASSIGNMENT_EXPRESSION(true, true, false);
			}
			else
			{
				// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
				output_token = -1;
				output_expr = null;
			}
			return new org.jawk.frontend.AwkParser.Print_AST(this, func_params, output_token, 
				output_expr);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST PRINTF_STATEMENT()
		{
			bool parens = (c == '(');
			ExpectKeyword("printf");
			org.jawk.frontend.AwkParser.AST func_params;
			int output_token;
			org.jawk.frontend.AwkParser.AST output_expr;
			if (parens)
			{
				Lexer();
				if (token == _CLOSE_PAREN_)
				{
					func_params = null;
				}
				else
				{
					func_params = EXPRESSION_LIST(false, true);
				}
				// NO comparators allowed, allow in expression
				Lexer(_CLOSE_PAREN_);
			}
			else
			{
				if (token == _NEWLINE_ || token == _SEMICOLON_ || token == _CLOSE_BRACE_ || token
					 == _CLOSE_PAREN_ || (token == _GT_ || token == _APPEND_ || token == _PIPE_))
				{
					func_params = null;
				}
				else
				{
					func_params = EXPRESSION_LIST(false, true);
				}
			}
			// NO comparators allowed, allow in expression
			if (token == _GT_ || token == _APPEND_ || token == _PIPE_)
			{
				output_token = token;
				Lexer();
				output_expr = ASSIGNMENT_EXPRESSION(true, true, false);
			}
			else
			{
				// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
				output_token = -1;
				output_expr = null;
			}
			return new org.jawk.frontend.AwkParser.Printf_AST(this, func_params, output_token
				, output_expr);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST GETLINE_EXPRESSION(org.jawk.frontend.AwkParser.AST
			 pipe_expr, bool allow_comparators, bool allow_in_keyword)
		{
			ExpectKeyword("getline");
			org.jawk.frontend.AwkParser.AST lvalue = LVALUE(allow_comparators, allow_in_keyword);
            //org.jawk.frontend.AwkParser.AST expr;
			if (token == _LT_)
			{
				Lexer();
				org.jawk.frontend.AwkParser.AST assignment_expr = ASSIGNMENT_EXPRESSION(allow_comparators
					, allow_in_keyword, false);
				// do NOT allow multidim indices expressions
				if (pipe_expr != null)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this, "Cannot have both pipe expression and redirect into a getline."
						);
				}
				return new org.jawk.frontend.AwkParser.Getline_AST(this, pipe_expr, lvalue, assignment_expr
					);
			}
			else
			{
				return new org.jawk.frontend.AwkParser.Getline_AST(this, pipe_expr, lvalue, null);
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST LVALUE(bool allow_comparators, bool
			 allow_in_keyword)
		{
			// false = do NOT allow multi dimension indices expressions
			if (token == _DOLLAR_)
			{
				return FACTOR(allow_comparators, allow_in_keyword, false);
			}
			if (token == _ID_)
			{
				return FACTOR(allow_comparators, allow_in_keyword, false);
			}
			return null;
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST DO_STATEMENT()
		{
			ExpectKeyword("do");
			Opt_newline();
			org.jawk.frontend.AwkParser.AST block = BLOCK_OR_STMT();
			if (token == _SEMICOLON_)
			{
				Lexer();
			}
			Opt_newline();
			ExpectKeyword("while");
			Lexer(_OPEN_PAREN_);
			org.jawk.frontend.AwkParser.AST expr = ASSIGNMENT_EXPRESSION(true, true, false);
			// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
			Lexer(_CLOSE_PAREN_);
			return new org.jawk.frontend.AwkParser.DoStatement_AST(this, block, expr);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST RETURN_STATEMENT()
		{
			ExpectKeyword("return");
			if (token == _SEMICOLON_ || token == _NEWLINE_ || token == _CLOSE_BRACE_)
			{
				return new org.jawk.frontend.AwkParser.ReturnStatement_AST(this, null);
			}
			else
			{
				return new org.jawk.frontend.AwkParser.ReturnStatement_AST(this, ASSIGNMENT_EXPRESSION
					(true, true, false));
			}
		}

		// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST EXIT_STATEMENT()
		{
			ExpectKeyword("exit");
			if (token == _SEMICOLON_ || token == _NEWLINE_ || token == _CLOSE_BRACE_)
			{
				return new org.jawk.frontend.AwkParser.ExitStatement_AST(this, null);
			}
			else
			{
				return new org.jawk.frontend.AwkParser.ExitStatement_AST(this, ASSIGNMENT_EXPRESSION
					(true, true, false));
			}
		}

		// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST SLEEP_STATEMENT()
		{
			bool parens = (c == '(');
			ExpectKeyword("_sleep");
			if (token == _SEMICOLON_ || token == _NEWLINE_ || token == _CLOSE_BRACE_)
			{
				return new org.jawk.frontend.AwkParser.SleepStatement_AST(this, null);
			}
			else
			{
				// allow for a blank param list: "()" using the parens boolean below
				// otherwise, the parser will complain because assignment_expression cannot be ()
				if (parens)
				{
					Lexer();
				}
				org.jawk.frontend.AwkParser.AST sleep_ast;
				if (token == _CLOSE_PAREN_)
				{
					sleep_ast = new org.jawk.frontend.AwkParser.SleepStatement_AST(this, null);
				}
				else
				{
					sleep_ast = new org.jawk.frontend.AwkParser.SleepStatement_AST(this, ASSIGNMENT_EXPRESSION
						(true, true, false));
				}
				// true = allow comparators, allow IN keyword, do NOT allow multidim indices expressions
				if (parens)
				{
					Lexer(_CLOSE_PAREN_);
				}
				return sleep_ast;
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST DUMP_STATEMENT()
		{
			bool parens = (c == '(');
			ExpectKeyword("_dump");
			if (token == _SEMICOLON_ || token == _NEWLINE_ || token == _CLOSE_BRACE_)
			{
				return new org.jawk.frontend.AwkParser.DumpStatement_AST(this, null);
			}
			else
			{
				if (parens)
				{
					Lexer();
				}
				org.jawk.frontend.AwkParser.AST dump_ast;
				if (token == _CLOSE_PAREN_)
				{
					dump_ast = new org.jawk.frontend.AwkParser.DumpStatement_AST(this, null);
				}
				else
				{
					dump_ast = new org.jawk.frontend.AwkParser.DumpStatement_AST(this, EXPRESSION_LIST
						(true, true));
				}
				// true = allow comparators, allow IN keyword
				if (parens)
				{
					Lexer(_CLOSE_PAREN_);
				}
				return dump_ast;
			}
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST NEXT_STATEMENT()
		{
			ExpectKeyword("next");
			return new org.jawk.frontend.AwkParser.NextStatement_AST(this);
		}

		/// <exception cref="System.IO.IOException"></exception>
		internal virtual org.jawk.frontend.AwkParser.AST CONTINUE_STATEMENT()
		{
			ExpectKeyword("continue");
			return new org.jawk.frontend.AwkParser.ContinueStatement_AST(this);
		}

		/// <exception cref="System.IO.IOException"></exception>
		private void ExpectKeyword(string keyword)
		{
			if (token == KEYWORDS[keyword])
			{
				Lexer();
			}
			else
			{
				throw new org.jawk.frontend.AwkParser.ParserException(this, "Expecting " + keyword
					 + ".  Got (" + token + "): " + text);
			}
		}

		internal abstract class AST : org.jawk.frontend.IAwkSyntaxTree
		{
            private int lineno = 0;

            public org.jawk.frontend.AwkParser.AST parent;

			public org.jawk.frontend.AwkParser.AST ast1;

            public org.jawk.frontend.AwkParser.AST ast2;

            public org.jawk.frontend.AwkParser.AST ast3;

            public org.jawk.frontend.AwkParser.AST ast4;

			// parser
			// ===============================================================================
			// AST class defs
			protected org.jawk.frontend.AwkParser.AST SearchFor(System.Type cls)
			{
				org.jawk.frontend.AwkParser.AST ptr = this;
				while (ptr != null)
				{
					if (cls.IsInstanceOfType(ptr))
					{
						return ptr;
					}
					ptr = ptr.parent;
				}
				return null;
			}

			protected AST(AwkParser _enclosing)
			{
				this._enclosing = _enclosing;
                lineno = _enclosing.reader.Line + 1;
                is_begin = IsBegin();
                is_end = IsEnd();
                is_function = IsFunction();
			}

			protected AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST ast1):this(_enclosing)
			{
				this.ast1 = ast1;
				if (ast1 != null)
				{
					ast1.parent = this;
				}
                is_begin = IsBegin();
                is_end = IsEnd();
                is_function = IsFunction();
			}

			protected AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST ast1, org.jawk.frontend.AwkParser.AST
				 ast2):this(_enclosing,ast1)
			{
				this.ast2 = ast2;
				if (ast2 != null)
				{
					ast2.parent = this;
				}
                is_begin = IsBegin();
                is_end = IsEnd();
                is_function = IsFunction();
			}

			protected AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST ast1, org.jawk.frontend.AwkParser.AST
                 ast2, org.jawk.frontend.AwkParser.AST ast3)
                : this(_enclosing, ast1,ast2)
			{
				this.ast3 = ast3;
				if (ast3 != null)
				{
					ast3.parent = this;
				}
                is_begin = IsBegin();
                is_end = IsEnd();
                is_function = IsFunction();
			}

			protected AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST ast1, org.jawk.frontend.AwkParser.AST
				 ast2, org.jawk.frontend.AwkParser.AST ast3, org.jawk.frontend.AwkParser.AST ast4
                )
                : this(_enclosing, ast1, ast2, ast3)
			{
				this.ast4 = ast4;
				if (ast4 != null)
				{
					ast4.parent = this;
				}
                is_begin = IsBegin();
                is_end = IsEnd();
                is_function = IsFunction();
			}

			/// <summary>
			/// Dump a meaningful text representation of this
			/// abstract syntax tree node to the output (print)
			/// stream.
			/// </summary>
			/// <remarks>
			/// Dump a meaningful text representation of this
			/// abstract syntax tree node to the output (print)
			/// stream.  Either it is called directly by the
			/// application program, or it is called by the
			/// parent node of this tree node.
			/// </remarks>
			/// <param name="ps">
			/// The print stream to dump the text
			/// representation.
			/// </param>
			public virtual void Dump(System.IO.TextWriter ps)
			{
				this.Dump(ps, 0);
			}

			private void Dump(System.IO.TextWriter ps, int lvl)
			{
				System.Text.StringBuilder spaces = new System.Text.StringBuilder();
				for (int i = 0; i < lvl; i++)
				{
					spaces.Append(' ');
				}
				ps.WriteLine(spaces + this.ToString());
				if (this.ast1 != null)
				{
					this.ast1.Dump(ps, lvl + 1);
				}
				if (this.ast2 != null)
				{
					this.ast2.Dump(ps, lvl + 1);
				}
				if (this.ast3 != null)
				{
					this.ast3.Dump(ps, lvl + 1);
				}
				if (this.ast4 != null)
				{
					this.ast4.Dump(ps, lvl + 1);
				}
			}

			/// <summary>Apply semantic checks to this node.</summary>
			/// <remarks>
			/// Apply semantic checks to this node.  The default
			/// implementation is to simply call semanticAnalysis()
			/// on all the children of this abstract syntax tree node.
			/// Therefore, this method must be overridden to provide
			/// meaningful semantic analysis / checks.
			/// </remarks>
			/// <exception cref="SemanticException">upon a semantic error.</exception>
			/// <exception cref="org.jawk.frontend.AwkParser.AST.SemanticException"></exception>
			public virtual void SemanticAnalysis()
			{
				if (this.ast1 != null)
				{
					this.ast1.SemanticAnalysis();
				}
				if (this.ast2 != null)
				{
					this.ast2.SemanticAnalysis();
				}
				if (this.ast3 != null)
				{
					this.ast3.SemanticAnalysis();
				}
				if (this.ast4 != null)
				{
					this.ast4.SemanticAnalysis();
				}
			}

			/// <summary>
			/// Appends tuples to the AwkTuples list
			/// for this abstract syntax tree node.
			/// </summary>
			/// <remarks>
			/// Appends tuples to the AwkTuples list
			/// for this abstract syntax tree node.  Subclasses
			/// must implement this method.
			/// <p>
			/// This is called either by the main program to generate a full
			/// list of tuples for the abstract syntax tree, or it is called
			/// by other abstract syntax tree nodes in response to their
			/// attempt at populating tuples.
			/// </remarks>
			/// <param name="tuples">The tuples to populate.</param>
			/// <returns>
			/// The number of items left on the stack after
			/// these tuples have executed.
			/// </returns>
			public abstract int PopulateTuples(org.jawk.intermediate.AwkTuples tuples);

			protected void PushSourceLineNumber(org.jawk.intermediate.AwkTuples tuples)
			{
				tuples.PushSourceLineNumber(this.lineno);
			}

			protected void PopSourceLineNumber(org.jawk.intermediate.AwkTuples tuples)
			{
				tuples.PopSourceLineNumber(this.lineno);
			}

            protected bool is_begin = false;

			public bool IsBegin()
			{
				bool result = this.is_begin;
				if (!result && this.ast1 != null)
				{
					result = this.ast1.IsBegin();
				}
				if (!result && this.ast2 != null)
				{
					result = this.ast2.IsBegin();
				}
				if (!result && this.ast3 != null)
				{
					result = this.ast3.IsBegin();
				}
				if (!result && this.ast4 != null)
				{
					result = this.ast4.IsBegin();
				}
				return result;
			}

			protected bool is_end = false;

			public bool IsEnd()
			{
				bool result = this.is_end;
				if (!result && this.ast1 != null)
				{
					result = this.ast1.IsEnd();
				}
				if (!result && this.ast2 != null)
				{
					result = this.ast2.IsEnd();
				}
				if (!result && this.ast3 != null)
				{
					result = this.ast3.IsEnd();
				}
				if (!result && this.ast4 != null)
				{
					result = this.ast4.IsEnd();
				}
				return result;
			}

			protected bool is_function = false;

			public bool IsFunction()
			{
				bool result = this.is_function;
				if (!result && this.ast1 != null)
				{
					result = this.ast1.IsFunction();
				}
				if (!result && this.ast2 != null)
				{
					result = this.ast2.IsFunction();
				}
				if (!result && this.ast3 != null)
				{
					result = this.ast3.IsFunction();
				}
				if (!result && this.ast4 != null)
				{
					result = this.ast4.IsFunction();
				}
				return result;
			}

			public virtual bool IsArray()
			{
				return false;
			}

			public virtual bool IsScalar()
			{
				return false;
			}

			/// <summary>Made protected so that subclasses can access it.</summary>
			/// <remarks>
			/// Made protected so that subclasses can access it.
			/// Package-level access was not necessary.
			/// </remarks>
			[System.Serializable]
			protected class SemanticException : System.Exception
			{
				internal SemanticException(AST _enclosing, string msg) : base(msg + " (line: " + _enclosing.lineno + ")")
				{
					this._enclosing = _enclosing;
				}

				private readonly AST _enclosing;
			}

			public void ThrowSemanticException(string msg)
			{
				throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, msg);
			}

			public override string ToString()
			{
				return this.GetType().FullName;//.ReplaceFirst(".*[$.]", string.Empty);
			}

			private readonly AwkParser _enclosing;
		}

		private abstract class ScalarExpression_AST : org.jawk.frontend.AwkParser.AST
		{
			protected ScalarExpression_AST(AwkParser _enclosing) : base(_enclosing)
			{
				this._enclosing = _enclosing;
			}

			protected ScalarExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                 a1)
                : base(_enclosing,a1)
			{
				this._enclosing = _enclosing;
			}

			protected ScalarExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                 a1, org.jawk.frontend.AwkParser.AST a2)
                : base(_enclosing,a1, a2)
			{
				this._enclosing = _enclosing;
			}

			protected ScalarExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 a1, org.jawk.frontend.AwkParser.AST a2, org.jawk.frontend.AwkParser.AST a3) : base
                (_enclosing,a1, a2, a3)
			{
				this._enclosing = _enclosing;
			}

			// abstract class AST [AwkSyntaxTree]
			public sealed override bool IsArray()
			{
				return false;
			}

			public sealed override bool IsScalar()
			{
				return true;
			}

			private readonly AwkParser _enclosing;
		}

		private static bool IsRule(org.jawk.frontend.AwkParser.AST ast)
		{
			return ast != null && !ast.IsBegin() && !ast.IsEnd() && !ast.IsFunction();
		}

		/// <summary>
		/// Inspects the action rule condition whether it contains
		/// extensions.
		/// </summary>
		/// <remarks>
		/// Inspects the action rule condition whether it contains
		/// extensions.  It does a superficial check of
		/// the abstract syntax tree of the action rule.
		/// In other words, it will not examine whether user-defined
		/// functions within the action rule contain extensions.
		/// </remarks>
		/// <param name="ast">The action rule expression to examine.</param>
		/// <returns>
		/// true if the action rule condition contains
		/// an extension; false otherwise.
		/// </returns>
		private static bool IsExtensionConditionRule(org.jawk.frontend.AwkParser.AST ast)
		{
			if (!IsRule(ast))
			{
				return false;
			}
			if (ast.ast1 == null)
			{
				return false;
			}
			if (!ContainsASTType(ast.ast1, typeof(org.jawk.frontend.AwkParser.Extension_AST)))
			{
				return false;
			}
			if (ContainsASTType(ast.ast1, new System.Type[] { typeof(org.jawk.frontend.AwkParser.FunctionCall_AST
				), typeof(org.jawk.frontend.AwkParser.DollarExpression_AST) }))
			{
				return false;
			}
			return true;
		}

		//return containsExtension(ast.ast1);
		private static bool ContainsASTType(org.jawk.frontend.AwkParser.AST ast, System.Type
			 cls)
		{
			return ContainsASTType(ast, new System.Type[] { cls });
		}

		private static bool ContainsASTType(org.jawk.frontend.AwkParser.AST ast, System.Type[] cls_array)
		{
			if (ast == null)
			{
				return false;
			}
			foreach (System.Type cls in cls_array)
			{
				if (cls.IsInstanceOfType(ast))
				{
					return true;
				}
			}
			if (ContainsASTType(ast.ast1, cls_array))
			{
				return true;
			}
			if (ContainsASTType(ast.ast2, cls_array))
			{
				return true;
			}
			if (ContainsASTType(ast.ast3, cls_array))
			{
				return true;
			}
			if (ContainsASTType(ast.ast4, cls_array))
			{
				return true;
			}
			return false;
		}

		private org.jawk.intermediate.IAddress next_address;

		private class RuleList_AST : org.jawk.frontend.AwkParser.AST
		{
			public RuleList_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST rule, 
				org.jawk.frontend.AwkParser.AST rest) : base(_enclosing, rule, rest)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				this._enclosing.next_address = tuples.CreateAddress("next_address");
				org.jawk.intermediate.IAddress exit_addr = tuples.CreateAddress("end blocks start address"
					);
				// goto start address
				org.jawk.intermediate.IAddress start_address = tuples.CreateAddress("start address"
					);
				tuples.SetExitAddress(exit_addr);
				tuples.GotoAddress(start_address);
				org.jawk.frontend.AwkParser.AST ptr;
				// compile functions
				ptr = this;
				while (ptr != null)
				{
					if (ptr.ast1 != null && ptr.ast1.IsFunction())
					{
						//assert ptr.ast1 != null;
						int ast1_count = ptr.ast1.PopulateTuples(tuples);
					}
					//assert ast1_count == 0;
					ptr = ptr.ast2;
				}
				// START OF MAIN BLOCK
				tuples.Address(start_address);
				// initialze special variables
				org.jawk.frontend.AwkParser.ID_AST nr_ast = this._enclosing.symbol_table.GetID("NR"
					);
				org.jawk.frontend.AwkParser.ID_AST fnr_ast = this._enclosing.symbol_table.GetID("FNR"
					);
				org.jawk.frontend.AwkParser.ID_AST nf_ast = this._enclosing.symbol_table.GetID("NF"
					);
				org.jawk.frontend.AwkParser.ID_AST fs_ast = this._enclosing.symbol_table.GetID("FS"
					);
				org.jawk.frontend.AwkParser.ID_AST rs_ast = this._enclosing.symbol_table.GetID("RS"
					);
				org.jawk.frontend.AwkParser.ID_AST ofs_ast = this._enclosing.symbol_table.GetID("OFS"
					);
				org.jawk.frontend.AwkParser.ID_AST rstart_ast = this._enclosing.symbol_table.GetID
					("RSTART");
				org.jawk.frontend.AwkParser.ID_AST rlength_ast = this._enclosing.symbol_table.GetID
					("RLENGTH");
				org.jawk.frontend.AwkParser.ID_AST filename_ast = this._enclosing.symbol_table.GetID
					("FILENAME");
				org.jawk.frontend.AwkParser.ID_AST subsep_ast = this._enclosing.symbol_table.GetID
					("SUBSEP");
				org.jawk.frontend.AwkParser.ID_AST convfmt_ast = this._enclosing.symbol_table.GetID
					("CONVFMT");
				org.jawk.frontend.AwkParser.ID_AST ofmt_ast = this._enclosing.symbol_table.GetID(
					"OFMT");
				org.jawk.frontend.AwkParser.ID_AST environ_ast = this._enclosing.symbol_table.GetID
					("ENVIRON");
				org.jawk.frontend.AwkParser.ID_AST argc_ast = this._enclosing.symbol_table.GetID(
					"ARGC");
				org.jawk.frontend.AwkParser.ID_AST argv_ast = this._enclosing.symbol_table.GetID(
					"ARGV");
				bool b = nr_ast.is_scalar = fnr_ast.is_scalar = nf_ast.is_scalar = fs_ast.is_scalar
					 = rs_ast.is_scalar = ofs_ast.is_scalar = rstart_ast.is_scalar = rlength_ast.is_scalar
					 = filename_ast.is_scalar = subsep_ast.is_scalar = convfmt_ast.is_scalar = ofmt_ast
					.is_scalar = environ_ast.is_array = argc_ast.is_scalar = argv_ast.is_array = true;
				// note!
				// note!
				// MUST BE DONE AFTER FUNCTIONS ARE COMPILED,
				// and after special variables are made known to the symbol table
				// (see above)!
				tuples.SetNumGlobals(this._enclosing.symbol_table.NumGlobals());
				tuples.NfOffset(nf_ast.offset);
				tuples.NrOffset(nr_ast.offset);
				tuples.FnrOffset(fnr_ast.offset);
				tuples.FsOffset(fs_ast.offset);
				tuples.RsOffset(rs_ast.offset);
				tuples.OfsOffset(ofs_ast.offset);
				tuples.RstartOffset(rstart_ast.offset);
				tuples.RlengthOffset(rlength_ast.offset);
				tuples.FilenameOffset(filename_ast.offset);
				tuples.SubsepOffset(subsep_ast.offset);
				tuples.ConvfmtOffset(convfmt_ast.offset);
				tuples.OfmtOffset(ofmt_ast.offset);
				tuples.EnvironOffset(environ_ast.offset);
				tuples.ArgcOffset(argc_ast.offset);
				tuples.ArgvOffset(argv_ast.offset);
				// grab all BEGINs
				ptr = this;
				// ptr.ast1 == blank rule condition (i.e.: { print })
				while (ptr != null)
				{
					if (ptr.ast1 != null && ptr.ast1.IsBegin())
					{
						//assert ptr.ast1 != null;
						int ast1_count = ptr.ast1.PopulateTuples(tuples);
					}
					//assert ast1_count == 0;
					ptr = ptr.ast2;
				}
				bool req_loop = false;
				bool req_input = false;
				ptr = this;
				while (req_input == false && ptr != null)
				{
					if (org.jawk.frontend.AwkParser.IsRule(ptr.ast1))
					{
						req_loop = true;
						if (!this._enclosing.no_input)
						{
							req_input = true;
						}
					}
					ptr = ptr.ast2;
				}
				if (req_loop)
				{
					org.jawk.intermediate.IAddress input_loop_address = null;
					org.jawk.intermediate.IAddress no_more_input = null;
					input_loop_address = tuples.CreateAddress("input_loop_address");
					tuples.Address(input_loop_address);
					ptr = this;
					if (req_input)
					{
						no_more_input = tuples.CreateAddress("no_more_input");
						tuples.ConsumeInput(no_more_input);
					}
					// grab all INPUT RULES
					while (ptr != null)
					{
						// the first one of these is an input rule
						if (org.jawk.frontend.AwkParser.IsRule(ptr.ast1))
						{
							//assert ptr.ast1 != null;
							int ast1_count = ptr.ast1.PopulateTuples(tuples);
						}
						//assert ast1_count == 0;
						ptr = ptr.ast2;
					}
					tuples.Address(this._enclosing.next_address);
					tuples.GotoAddress(input_loop_address);
					if (req_input)
					{
						tuples.Address(no_more_input);
						// compiler has issue with missing nop here
						tuples.Nop();
					}
				}
				// indicate where the first end block resides
				// in the event of an exit statement
				tuples.Address(exit_addr);
				tuples.SetWithinEndBlocks(true);
				// grab all ENDs
				ptr = this;
				while (ptr != null)
				{
					if (ptr.ast1 != null && ptr.ast1.IsEnd())
					{
						//assert ptr.ast1 != null;
						int ast1_count = ptr.ast1.PopulateTuples(tuples);
					}
					//assert ast1_count == 0;
					ptr = ptr.ast2;
				}
				// force a nop here to resolve any addresses that haven't been resolved yet
				// (i.e., no_more_input wouldn't be resolved if there are no END{} blocks)
				tuples.Nop();
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class Rule_AST : org.jawk.frontend.AwkParser.AST, org.jawk.frontend.AwkParser.INextable
		{
			public Rule_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST opt_expression
				, org.jawk.frontend.AwkParser.AST opt_rule) : base(_enclosing,opt_expression, opt_rule)
			{
				this._enclosing = _enclosing;
			}

			// made non-static to access the "next_address" field of the frontend
			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				if (this.ast1 == null)
				{
					// just indicate to execute the rule
					tuples.Push(1);
				}
				else
				{
					// 1 == true
					int result = this.ast1.PopulateTuples(tuples);
				}
				//assert result == 1;
				// result of whether to execute or not is on the stack
				org.jawk.intermediate.IAddress bypass_rule = tuples.CreateAddress("bypass_rule");
				tuples.IfFalse(bypass_rule);
				// execute the opt_rule here!
				if (this.ast2 == null)
				{
					if (this._enclosing.no_input)
					{
					}
					else
					{
						// with -ni, no default blank rule of print $0
						if (this.ast1 == null || !this.ast1.IsBegin() && !this.ast1.IsEnd())
						{
							// display $0
							tuples.Print(0);
						}
					}
				}
				else
				{
					// else, don't populate it with anything
					// (i.e., blank BEGIN/END rule)
					// execute it, and leave nothing on the stack
					int ast2_count = this.ast2.PopulateTuples(tuples);
				}
				//assert ast2_count == 0;
				tuples.Address(bypass_rule).Nop();
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			public virtual org.jawk.intermediate.IAddress NextAddress()
			{
				if (!org.jawk.frontend.AwkParser.IsRule(this))
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Must call next within an input rule."
						);
				}
				if (this._enclosing.next_address == null)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot call next here."
						);
				}
				return this._enclosing.next_address;
			}

			private readonly AwkParser _enclosing;
		}

		private class IfStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public IfStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr
				, org.jawk.frontend.AwkParser.AST b1, org.jawk.frontend.AwkParser.AST b2) : base
				(_enclosing, expr, b1, b2)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				org.jawk.intermediate.IAddress elseblock = tuples.CreateAddress("elseblock");
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.IfFalse(elseblock);
				if (this.ast2 != null)
				{
					int ast2_result = this.ast2.PopulateTuples(tuples);
				}
				//assert ast2_result == 0;
				if (this.ast3 == null)
				{
					tuples.Address(elseblock);
				}
				else
				{
					org.jawk.intermediate.IAddress end = tuples.CreateAddress("end");
					tuples.GotoAddress(end);
					tuples.Address(elseblock);
					int ast3_result = this.ast3.PopulateTuples(tuples);
					//assert ast3_result == 0;
					tuples.Address(end);
				}
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class TertiaryExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public TertiaryExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 a1, org.jawk.frontend.AwkParser.AST a2, org.jawk.frontend.AwkParser.AST a3) : base
				(_enclosing,a1, a2, a3)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1!=null;
				//assert ast2!=null;
				//assert ast3!=null;
				org.jawk.intermediate.IAddress elseexpr = tuples.CreateAddress("elseexpr");
				org.jawk.intermediate.IAddress end_tertiary = tuples.CreateAddress("end_tertiary"
					);
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.IfFalse(elseexpr);
				int ast2_result = this.ast2.PopulateTuples(tuples);
				//assert ast2_result == 1;
				tuples.GotoAddress(end_tertiary);
				tuples.Address(elseexpr);
				int ast3_result = this.ast3.PopulateTuples(tuples);
				//assert ast3_result == 1;
				tuples.Address(end_tertiary);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class WhileStatement_AST : org.jawk.frontend.AwkParser.AST, org.jawk.frontend.AwkParser.IBreakable
			, org.jawk.frontend.AwkParser.IContinueable
		{
			private org.jawk.intermediate.IAddress break_address;

			private org.jawk.intermediate.IAddress continue_address;

			// to satisfy the Breakable interface
			public virtual org.jawk.intermediate.IAddress BreakAddress()
			{
				//assert break_address != null;
				return this.break_address;
			}

			// to satisfy the Continueable interface
			public virtual org.jawk.intermediate.IAddress ContinueAddress()
			{
				//assert continue_address != null;
				return this.continue_address;
			}

			public WhileStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST 
				expr, org.jawk.frontend.AwkParser.AST block) : base(_enclosing, expr, block)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				this.break_address = tuples.CreateAddress("break_address");
				// LOOP
				org.jawk.intermediate.IAddress loop = tuples.CreateAddress("loop");
				tuples.Address(loop);
				// for while statements, the start-of-loop is the continue jump address
				this.continue_address = loop;
				// condition
				//assert (ast1 != null);
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.IfFalse(this.break_address);
				if (this.ast2 != null)
				{
					int ast2_result = this.ast2.PopulateTuples(tuples);
				}
				//assert ast2_result == 0;
				tuples.GotoAddress(loop);
				tuples.Address(this.break_address);
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class DoStatement_AST : org.jawk.frontend.AwkParser.AST, org.jawk.frontend.AwkParser.IBreakable
			, org.jawk.frontend.AwkParser.IContinueable
		{
			private org.jawk.intermediate.IAddress break_address;

			private org.jawk.intermediate.IAddress continue_address;

			// to satisfy the Breakable interface
			public virtual org.jawk.intermediate.IAddress BreakAddress()
			{
				//assert break_address != null;
				return this.break_address;
			}

			// to satisfy the Continueable interface
			public virtual org.jawk.intermediate.IAddress ContinueAddress()
			{
				//assert continue_address != null;
				return this.continue_address;
			}

			public DoStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST block
				, org.jawk.frontend.AwkParser.AST expr) : base(_enclosing, block, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				this.break_address = tuples.CreateAddress("break_address");
				this.continue_address = tuples.CreateAddress("continue_address");
				// LOOP
				org.jawk.intermediate.IAddress loop = tuples.CreateAddress("loop");
				tuples.Address(loop);
				if (this.ast1 != null)
				{
					int ast1_result = this.ast1.PopulateTuples(tuples);
				}
				//assert ast1_result == 0;
				// for do-while statements, the continue jump address is the loop condition
				tuples.Address(this.continue_address);
				// condition
				//assert(ast2 != null);
				int ast2_result = this.ast2.PopulateTuples(tuples);
				//assert ast2_result == 1;
				tuples.IfTrue(loop);
				//tuples.gotoAddress(loop);
				tuples.Address(this.break_address);
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class ForStatement_AST : org.jawk.frontend.AwkParser.AST, org.jawk.frontend.AwkParser.IBreakable
			, org.jawk.frontend.AwkParser.IContinueable
		{
			private org.jawk.intermediate.IAddress break_address;

			private org.jawk.intermediate.IAddress continue_address;

			// to satisfy the Breakable interface
			public virtual org.jawk.intermediate.IAddress BreakAddress()
			{
				//assert break_address != null;
				return this.break_address;
			}

			// to satisfy the Continueable interface
			public virtual org.jawk.intermediate.IAddress ContinueAddress()
			{
				//assert continue_address != null;
				return this.continue_address;
			}

			public ForStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr1
				, org.jawk.frontend.AwkParser.AST expr2, org.jawk.frontend.AwkParser.AST expr3, 
				org.jawk.frontend.AwkParser.AST block) : base(_enclosing, expr1, expr2, expr3, block)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				this.break_address = tuples.CreateAddress("break_address");
				this.continue_address = tuples.CreateAddress("continue_address");
				// initial actions
				if (this.ast1 != null)
				{
					int ast1_result = this.ast1.PopulateTuples(tuples);
					for (int i = 0; i < ast1_result; i++)
					{
						tuples.Pop();
					}
				}
				// LOOP
				org.jawk.intermediate.IAddress loop = tuples.CreateAddress("loop");
				tuples.Address(loop);
				if (this.ast2 != null)
				{
					// condition
					//assert(ast2 != null);
					int ast2_result = this.ast2.PopulateTuples(tuples);
					//assert ast2_result == 1;
					tuples.IfFalse(this.break_address);
				}
				if (this.ast4 != null)
				{
					// post loop action
					int ast4_result = this.ast4.PopulateTuples(tuples);
				}
				//assert ast4_result == 0;
				// for for-loops, the continue jump address is the post-loop-action
				tuples.Address(this.continue_address);
				// post-loop action
				if (this.ast3 != null)
				{
					int ast3_result = this.ast3.PopulateTuples(tuples);
					for (int i = 0; i < ast3_result; i++)
					{
						tuples.Pop();
					}
				}
				tuples.GotoAddress(loop);
				tuples.Address(this.break_address);
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class ForInStatement_AST : org.jawk.frontend.AwkParser.AST, org.jawk.frontend.AwkParser.IBreakable
			, org.jawk.frontend.AwkParser.IContinueable
		{
			private org.jawk.intermediate.IAddress break_address;

			private org.jawk.intermediate.IAddress continue_address;

			// to satisfy the Breakable interface
			public virtual org.jawk.intermediate.IAddress BreakAddress()
			{
				//assert break_address != null;
				return this.break_address;
			}

			// to satisfy the Continueable interface
			public virtual org.jawk.intermediate.IAddress ContinueAddress()
			{
				//assert continue_address != null;
				return this.continue_address;
			}

			public ForInStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST 
				key_id_ast, org.jawk.frontend.AwkParser.AST array_id_ast, org.jawk.frontend.AwkParser.AST
				 block) : base(_enclosing, key_id_ast, array_id_ast, block)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				org.jawk.frontend.AwkParser.ID_AST array_id_ast = (org.jawk.frontend.AwkParser.ID_AST
					)this.ast2;
				if (array_id_ast.IsScalar())
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, array_id_ast + 
						" is not an array");
				}
				array_id_ast.SetArray(true);
				this.break_address = tuples.CreateAddress("break_address");
				// push array onto the stack
				//assert ast2 != null;
				int ast2_result = this.ast2.PopulateTuples(tuples);
				// pops the array and pushes the keyset
				tuples.Keylist();
				// stack now contains:
				//
				// keylist
				// LOOP
				org.jawk.intermediate.IAddress loop = tuples.CreateAddress("loop");
				tuples.Address(loop);
				// for for-in loops, the continue jump address is the start-of-loop address
				this.continue_address = loop;
				//assert tuples.checkClass(KeyList.class);
				// condition
				tuples.Dup();
				tuples.IsEmptyList(this.break_address);
				//assert tuples.checkClass(KeyList.class);
				// take an element off the set
				tuples.Dup();
				tuples.GetFirstAndRemoveFromList();
				// assign it to the id
				tuples.Assign(((org.jawk.frontend.AwkParser.ID_AST)this.ast1).offset, ((org.jawk.frontend.AwkParser.ID_AST
					)this.ast1).is_global);
				tuples.Pop();
				// remove the assignment result
				if (this.ast3 != null)
				{
					// execute the block
					int ast3_result = this.ast3.PopulateTuples(tuples);
				}
				//assert ast3_result == 0;
				// otherwise, there is no block to execute
				//assert tuples.checkClass(KeyList.class);
				tuples.GotoAddress(loop);
				tuples.Address(this.break_address);
				tuples.Pop();
				// keylist
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class EmptyStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			private EmptyStatement_AST(AwkParser _enclosing) : base(_enclosing)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				// nothing to populate!
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		/// <summary>The AST for an expression used as a statement.</summary>
		/// <remarks>
		/// The AST for an expression used as a statement.
		/// If the expression returns a value, the value is popped
		/// off the stack and discarded.
		/// </remarks>
		private class ExpressionStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public ExpressionStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 expr) : base(_enclosing,expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int expr_count = this.ast1.PopulateTuples(tuples);
				if (expr_count == 0)
				{
				}
				else
				{
					if (expr_count == 1)
					{
						tuples.Pop();
					}
					else
					{
						//assert false : "expr_count = "+expr_count;
						this.PopSourceLineNumber(tuples);
					}
				}
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class AssignmentExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			/// <summary>operand / operator</summary>
			private int op;

			private string text;

			public AssignmentExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 lhs, int op, string text, org.jawk.frontend.AwkParser.AST rhs) : base(_enclosing,lhs, rhs)
			{
				this._enclosing = _enclosing;
				this.op = op;
				this.text = text;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.op + "/" + this.text + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast2 != null;
				int ast2_count = this.ast2.PopulateTuples(tuples);
				//assert ast2_count == 1;
				// here, stack contains one value
				if (this.ast1 is org.jawk.frontend.AwkParser.ID_AST)
				{
					org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
						.ast1;
					if (id_ast.IsArray())
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot use " +
							 id_ast + " as a scalar.  It is an array.");
					}
					id_ast.SetScalar(true);
					if (this.op == org.jawk.frontend.AwkParser._EQUALS_)
					{
						// Expected side effect:
						// Upon assignment, if the var is RS, reapply RS to input streams.
						tuples.Assign(id_ast.offset, id_ast.is_global);
					}
					else
					{
						if (this.op == org.jawk.frontend.AwkParser._PLUS_EQ_)
						{
							tuples.PlusEq(id_ast.offset, id_ast.is_global);
						}
						else
						{
							if (this.op == org.jawk.frontend.AwkParser._MINUS_EQ_)
							{
								tuples.MinusEq(id_ast.offset, id_ast.is_global);
							}
							else
							{
								if (this.op == org.jawk.frontend.AwkParser._MULT_EQ_)
								{
									tuples.MultEq(id_ast.offset, id_ast.is_global);
								}
								else
								{
									if (this.op == org.jawk.frontend.AwkParser._DIV_EQ_)
									{
										tuples.DivEq(id_ast.offset, id_ast.is_global);
									}
									else
									{
										if (this.op == org.jawk.frontend.AwkParser._MOD_EQ_)
										{
											tuples.ModEq(id_ast.offset, id_ast.is_global);
										}
										else
										{
											if (this.op == org.jawk.frontend.AwkParser._POW_EQ_)
											{
												tuples.PowEq(id_ast.offset, id_ast.is_global);
											}
											else
											{
												throw new System.Exception("Unhandled op: " + this.op + " / " + this.text);
											}
										}
									}
								}
							}
						}
					}
					if (id_ast.id.Equals("RS"))
					{
						tuples.ApplyRS();
					}
				}
				else
				{
					if (this.ast1 is org.jawk.frontend.AwkParser.ArrayReference_AST)
					{
						org.jawk.frontend.AwkParser.ArrayReference_AST arr = (org.jawk.frontend.AwkParser.ArrayReference_AST
							)this.ast1;
						// push the index
						//assert arr.ast2 != null;
						int arr_ast2_result = arr.ast2.PopulateTuples(tuples);
						//assert arr_ast2_result == 1;
						// push the array ref itself
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)arr
							.ast1;
						if (id_ast.IsScalar())
						{
							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot use " +
								 id_ast + " as an array.  It is a scalar.");
						}
						id_ast.SetArray(true);
						
							if (this.op == org.jawk.frontend.AwkParser._EQUALS_)
							{
								tuples.AssignArray(id_ast.offset, id_ast.is_global);
							}
							else
							{
								if (this.op == org.jawk.frontend.AwkParser._PLUS_EQ_)
								{
									tuples.PlusEqArray(id_ast.offset, id_ast.is_global);
								}
								else
								{
									if (this.op == org.jawk.frontend.AwkParser._MINUS_EQ_)
									{
										tuples.MinusEqArray(id_ast.offset, id_ast.is_global);
									}
									else
									{
										if (this.op == org.jawk.frontend.AwkParser._MULT_EQ_)
										{
											tuples.MultEqArray(id_ast.offset, id_ast.is_global);
										}
										else
										{
											if (this.op == org.jawk.frontend.AwkParser._DIV_EQ_)
											{
												tuples.DivEqArray(id_ast.offset, id_ast.is_global);
											}
											else
											{
												if (this.op == org.jawk.frontend.AwkParser._MOD_EQ_)
												{
													tuples.ModEqArray(id_ast.offset, id_ast.is_global);
												}
												else
												{
													if (this.op == org.jawk.frontend.AwkParser._POW_EQ_)
													{
														tuples.PowEqArray(id_ast.offset, id_ast.is_global);
													}
													else
													{
														throw new org.jawk.NotImplementedError("Unhandled op: " + this.op + " / " + this.
															text + " for arrays.");
													}
												}
											}
										}
									}
								}
							}
						
					}
					else
					{
						if (this.ast1 is org.jawk.frontend.AwkParser.DollarExpression_AST)
						{
							org.jawk.frontend.AwkParser.DollarExpression_AST dollar_expr = (org.jawk.frontend.AwkParser.DollarExpression_AST
								)this.ast1;
							//assert dollar_expr.ast1 != null;
							int ast1_result = dollar_expr.ast1.PopulateTuples(tuples);
							//assert ast1_result == 1;
							// stack contains eval of dollar arg
							if (this.op == org.jawk.frontend.AwkParser._EQUALS_)
							{
								tuples.AssignAsInputField();
							}
							else
							{
								if (this.op == org.jawk.frontend.AwkParser._PLUS_EQ_)
								{
									tuples.PlusEqInputField();
								}
								else
								{
									if (this.op == org.jawk.frontend.AwkParser._MINUS_EQ_)
									{
										tuples.MinusEqInputField();
									}
									else
									{
										if (this.op == org.jawk.frontend.AwkParser._MULT_EQ_)
										{
											tuples.MultEqInputField();
										}
										else
										{
											if (this.op == org.jawk.frontend.AwkParser._DIV_EQ_)
											{
												tuples.DivEqInputField();
											}
											else
											{
												if (this.op == org.jawk.frontend.AwkParser._MOD_EQ_)
												{
													tuples.ModEqInputField();
												}
												else
												{
													if (this.op == org.jawk.frontend.AwkParser._POW_EQ_)
													{
														tuples.PowEqInputField();
													}
													else
													{
														throw new org.jawk.NotImplementedError("Unhandled op: " + this.op + " / " + this.
															text + " for dollar expressions.");
													}
												}
											}
										}
									}
								}
							}
						}
						else
						{
							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot perform an assignment on: "
								 + this.ast1);
						}
					}
				}
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class InExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public InExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST arg
				, org.jawk.frontend.AwkParser.AST arr) : base(_enclosing,arg, arr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				//assert ast2 != null;
				if (!(this.ast2 is org.jawk.frontend.AwkParser.ID_AST))
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Expecting an array for rhs of IN.  Got an expression."
						);
				}
				org.jawk.frontend.AwkParser.ID_AST arr_ast = (org.jawk.frontend.AwkParser.ID_AST)
					this.ast2;
				if (arr_ast.IsScalar())
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Expecting an array for rhs of IN.  Got a scalar."
						);
				}
				arr_ast.SetArray(true);
				int ast2_result = arr_ast.PopulateTuples(tuples);
				//assert ast2_result == 1;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.IsIn();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class ComparisonExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			/// <summary>operand / operator</summary>
			private int op;

			private string text;

			public ComparisonExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 lhs, int op, string text, org.jawk.frontend.AwkParser.AST rhs) : base(_enclosing,lhs, rhs)
			{
				this._enclosing = _enclosing;
				this.op = op;
				this.text = text;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.op + "/" + this.text + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				//assert ast2 != null;
				int ast2_result = this.ast2.PopulateTuples(tuples);
				//assert ast2_result == 1;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				// 2 values on the stack
				if (this.op == org.jawk.frontend.AwkParser._EQ_)
				{
					tuples.CmpEq();
				}
				else
				{
					if (this.op == org.jawk.frontend.AwkParser._NE_)
					{
						tuples.CmpEq();
						tuples.Not();
					}
					else
					{
						if (this.op == org.jawk.frontend.AwkParser._LT_)
						{
							tuples.CmpLt();
						}
						else
						{
							if (this.op == org.jawk.frontend.AwkParser._GT_)
							{
								tuples.CmpGt();
							}
							else
							{
								if (this.op == org.jawk.frontend.AwkParser._LE_)
								{
									tuples.CmpGt();
									tuples.Not();
								}
								else
								{
									if (this.op == org.jawk.frontend.AwkParser._GE_)
									{
										tuples.CmpLt();
										tuples.Not();
									}
									else
									{
										if (this.op == org.jawk.frontend.AwkParser._MATCHES_)
										{
											tuples.Matches();
										}
										else
										{
											if (this.op == org.jawk.frontend.AwkParser._NOT_MATCHES_)
											{
												tuples.Matches();
												tuples.Not();
											}
											else
											{
												throw new System.Exception("Unhandled op: " + this.op + " / " + this.text);
											}
										}
									}
								}
							}
						}
					}
				}
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class LogicalExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			/// <summary>operand / operator</summary>
			private int op;

			private string text;

			public LogicalExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 lhs, int op, string text, org.jawk.frontend.AwkParser.AST rhs) : base(_enclosing,lhs, rhs)
			{
				this._enclosing = _enclosing;
				this.op = op;
				this.text = text;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.op + "/" + this.text + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				// exhibit short-circuit behavior
				org.jawk.intermediate.IAddress end = tuples.CreateAddress("end");
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.Dup();
				if (this.op == org.jawk.frontend.AwkParser._OR_)
				{
					// short_circuit when op is OR and 1st arg is true
					tuples.IfTrue(end);
				}
				else
				{
					if (this.op == org.jawk.frontend.AwkParser._AND_)
					{
						tuples.IfFalse(end);
					}
					else
					{
						//assert false : "Invalid op: "+op+" / "+text;
						tuples.Pop();
					}
				}
				int ast2_result = this.ast2.PopulateTuples(tuples);
				//assert ast2_result == 1;
				tuples.Address(end);
				// turn the result into boolean one or zero
				tuples.ToNumber();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class BinaryExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			/// <summary>operand / operator</summary>
			public int op;

			private string text;

			public BinaryExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 lhs, int op, string text, org.jawk.frontend.AwkParser.AST rhs) : base(_enclosing, lhs, rhs)
			{
				this._enclosing = _enclosing;
				this.op = op;
				this.text = text;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.op + "/" + this.text + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				int ast2_result = this.ast2.PopulateTuples(tuples);
				//assert ast2_result == 1;
				tuples.Swap();
				
					if (this.op == org.jawk.frontend.AwkParser._PLUS_)
					{
						tuples.Add();
					}
					else
					{
						if (this.op == org.jawk.frontend.AwkParser._MINUS_)
						{
							tuples.Subtract();
						}
						else
						{
							if (this.op == org.jawk.frontend.AwkParser._MULT_)
							{
								tuples.Multiply();
							}
							else
							{
								if (this.op == org.jawk.frontend.AwkParser._DIVIDE_)
								{
									tuples.Divide();
								}
								else
								{
									if (this.op == org.jawk.frontend.AwkParser._MOD_)
									{
										tuples.Mod();
									}
									else
									{
										if (this.op == org.jawk.frontend.AwkParser._POW_)
										{
											tuples.Pow();
										}
										else
										{
											throw new System.Exception("Unhandled op: " + this.op + " / " + this);
										}
									}
								}
							}
						}
					}
				
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class ConcatExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			internal ConcatExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                 lhs, org.jawk.frontend.AwkParser.AST rhs)
                : base(_enclosing, lhs, rhs)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast2 != null;
				int rhs_count = this.ast2.PopulateTuples(tuples);
				//assert rhs_count == 1;
				//assert ast1 != null;
				int lhs_count = this.ast1.PopulateTuples(tuples);
				//assert lhs_count == 1;
				tuples.Concat();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class NegativeExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public NegativeExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                 expr)
                : base(_enclosing, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.Negate();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class NotExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public NotExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr
                )
                : base(_enclosing, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.Not();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class DollarExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public DollarExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                 expr)
                : base(_enclosing, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.GetInputField();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class ArrayIndex_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public ArrayIndex_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr_ast
                , org.jawk.frontend.AwkParser.AST next)
                : base(_enclosing, expr_ast, next)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				org.jawk.frontend.AwkParser.AST ptr = this;
				int cnt = 0;
				while (ptr != null)
				{
					//assert ptr.ast1 != null;
					int ptr_ast1_result = ptr.ast1.PopulateTuples(tuples);
					//assert ptr_ast1_result == 1;
					++cnt;
					ptr = ptr.ast2;
				}
				//assert cnt >= 1;
				if (cnt > 1)
				{
					tuples.ApplySubsep(cnt);
				}
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class STATEMENTLIST_AST : org.jawk.frontend.AwkParser.AST
		{
			public STATEMENTLIST_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST statement_ast
                , org.jawk.frontend.AwkParser.AST rest)
                : base(_enclosing, statement_ast, rest)
			{
				this._enclosing = _enclosing;
			}

			// made classname all capitals to stand out in a syntax tree dump
			/// <summary>Recursively process statements within this statement list.</summary>
			/// <remarks>
			/// Recursively process statements within this statement list.
			/// <p>
			/// It originally was done linearly.  However, quirks in the grammar required
			/// a more general, recursive approach to processing this "list".
			/// <p>
			/// Note: this should be reevaluated periodically in case the grammar
			/// becomes linear again.
			/// </remarks>
			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				// typical recursive processing of a list
				//assert ast1 != null;
				int ast1_count = this.ast1.PopulateTuples(tuples);
				//assert ast1_count == 0;
				if (this.ast2 != null)
				{
					int ast2_count = this.ast2.PopulateTuples(tuples);
				}
				//assert ast2_count == 0;
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			public override string ToString()
			{
				return base.ToString() + " <" + this.ast1 + ">";
			}

			private readonly AwkParser _enclosing;
		}

		private interface IReturnable
		{
			org.jawk.intermediate.IAddress ReturnAddress();
		}

		private class FunctionDef_AST : org.jawk.frontend.AwkParser.AST, org.jawk.frontend.AwkParser.IReturnable
		{
			private string id;

			private org.jawk.intermediate.IAddress function_address;

			private org.jawk.intermediate.IAddress return_address;

			// made non-static to access the symbol table
			// to satisfy the Returnable interface
			public virtual org.jawk.intermediate.IAddress ReturnAddress()
			{
				//assert return_address != null;
				return this.return_address;
			}

			public FunctionDef_AST(AwkParser _enclosing, string id, org.jawk.frontend.AwkParser.AST
                 @params, org.jawk.frontend.AwkParser.AST func_body)
                : base(_enclosing, @params, func_body)
			{
				this._enclosing = _enclosing;
				this.id = id;
				this.is_function = true;
			}

			public virtual org.jawk.intermediate.IAddress GetAddress()
			{
				//assert function_address != null;
				return this.function_address;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				this.function_address = tuples.CreateAddress("function: " + this.id);
				this.return_address = tuples.CreateAddress("return_address for " + this.id);
				// annotate the tuple list
				// (useful for compilation,
				// not necessary for interpretation)
				tuples.Function(this.id, this.ParamCount());
				// function_address refers to first function body statement
				// rather than to function def opcode because during
				// interpretation, the function definition is a nop,
				// and for compilation, the next match of the function
				// name can be used
				tuples.Address(this.function_address);
				// the stack contains the parameters to the function call (in rev order, which is good)
				// execute the body
				// (function body could be empty [no statements])
				if (this.ast2 != null)
				{
					int ast2_result = this.ast2.PopulateTuples(tuples);
				}
				//assert ast2_result == 0 || ast2_result == 1;
				tuples.Address(this.return_address);
				tuples.ReturnFromFunction();
				/////////////////////////////////////////////
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			internal virtual int ParamCount()
			{
				org.jawk.frontend.AwkParser.AST ptr = this.ast1;
				int count = 0;
				while (ptr != null)
				{
					++count;
					ptr = ptr.ast1;
				}
				return count;
			}

			internal virtual void CheckActualToFormalParameters(org.jawk.frontend.AwkParser.AST
				 actual_param_list)
			{
				org.jawk.frontend.AwkParser.AST a_ptr = actual_param_list;
				org.jawk.frontend.AwkParser.FunctionDefParamList_AST f_ptr = (org.jawk.frontend.AwkParser.FunctionDefParamList_AST
					)this.ast1;
				while (a_ptr != null)
				{
					// actual parameter
					org.jawk.frontend.AwkParser.AST aparam = a_ptr.ast1;
					// formal function parameter
					org.jawk.frontend.AwkParser.AST fparam = this._enclosing.symbol_table.GetFunctionParameterIDAST
						(this.id, f_ptr.id);
					if (aparam.IsArray())
					{
						if (fparam.IsScalar())
						{
							aparam.ThrowSemanticException(this.id + ": Actual parameter (" + aparam + ") is an array, but formal parameter is used like a scalar."
								);
						}
					}
					if (aparam.IsScalar())
					{
						if (fparam.IsArray())
						{
							aparam.ThrowSemanticException(this.id + ": Actual parameter (" + aparam + ") is a scalar, but formal parameter is used like an array."
								);
						}
					}
					// condition parameters appropriately
					// (based on function parameter semantics)
					if (aparam is org.jawk.frontend.AwkParser.ID_AST)
					{
						org.jawk.frontend.AwkParser.ID_AST aparam_id_ast = (org.jawk.frontend.AwkParser.ID_AST
							)aparam;
						if (fparam.IsScalar())
						{
							aparam_id_ast.SetScalar(true);
						}
						if (fparam.IsArray())
						{
							aparam_id_ast.SetArray(true);
						}
					}
					// next
					a_ptr = a_ptr.ast2;
					f_ptr = (org.jawk.frontend.AwkParser.FunctionDefParamList_AST)f_ptr.ast1;
				}
			}

			private readonly AwkParser _enclosing;
		}

		private class FunctionCall_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			private org.jawk.frontend.AwkParser.FunctionProxy function_proxy;

			public FunctionCall_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.FunctionProxy
                 function_proxy, org.jawk.frontend.AwkParser.AST @params)
                : base(_enclosing, @params)
			{
				this._enclosing = _enclosing;
				// private class FunctionDef_AST {AST} [Returnable]
				this.function_proxy = function_proxy;
			}

			/// <summary>
			/// Applies several semantic checks with respect
			/// to user-defined-function calls.
			/// </summary>
			/// <remarks>
			/// Applies several semantic checks with respect
			/// to user-defined-function calls.
			/// <p>
			/// The checks performed are:
			/// <ul>
			/// <li>Make sure the function is defined.
			/// <li>The number of actual parameters does not
			/// exceed the number of formal parameters.
			/// <li>Matches actual parameters to formal parameter
			/// usage with respect to whether they are
			/// scalars, arrays, or either.
			/// (This determination is based on how
			/// the formal parameters are used within
			/// the function block.)
			/// </ul>
			/// A failure of any one of these checks
			/// results in a SemanticException.
			/// </remarks>
			/// <exception cref="SemanticException">
			/// upon a failure of
			/// any of the semantic checks specified above.
			/// </exception>
			/// <exception cref="org.jawk.frontend.AwkParser.AST.SemanticException"></exception>
			public override void SemanticAnalysis()
			{
				if (!this.function_proxy.IsDefined())
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "function " + this
						.function_proxy + " not defined");
				}
				int actual_param_count;
				if (this.ast1 == null)
				{
					actual_param_count = 0;
				}
				else
				{
					actual_param_count = this.ActualParamCount();
				}
				int formal_param_count = this.function_proxy.GetFunctionParamCount();
				if (formal_param_count < actual_param_count)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "the " + this.function_proxy.GetFunctionName() + " function" + " only accepts at most " + formal_param_count
						 + " parameter(s), not " + actual_param_count);
				}
				if (this.ast1 != null)
				{
					this.function_proxy.CheckActualToFormalParameters(this.ast1);
				}
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				if (!this.function_proxy.IsDefined())
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "function " + this
						.function_proxy + " not defined");
				}
				tuples.ScriptThis();
				int actual_param_count;
				if (this.ast1 == null)
				{
					actual_param_count = 0;
				}
				else
				{
					actual_param_count = this.ast1.PopulateTuples(tuples);
				}
				int formal_param_count = this.function_proxy.GetFunctionParamCount();
				if (formal_param_count < actual_param_count)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "the " + this.function_proxy
						.GetFunctionName() + " function" + " only accepts at most " + formal_param_count
						 + " parameter(s), not " + actual_param_count);
				}
				this.function_proxy.CheckActualToFormalParameters(this.ast1);
				tuples.CallFunction(this.function_proxy, this.function_proxy.GetFunctionName(), formal_param_count
					, actual_param_count);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private int ActualParamCount()
			{
				int cnt = 0;
				org.jawk.frontend.AwkParser.AST ptr = this.ast1;
				while (ptr != null)
				{
					//assert ptr.ast1 != null;
					++cnt;
					ptr = ptr.ast2;
				}
				return cnt;
			}

			private readonly AwkParser _enclosing;
		}

		private class BuiltinFunctionCall_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			private string id;

			private int f_idx;

			public BuiltinFunctionCall_AST(AwkParser _enclosing, string id, org.jawk.frontend.AwkParser.AST
                 @params)
                : base(_enclosing, @params)
			{
				this._enclosing = _enclosing;
				this.id = id;
				//assert BUILTIN_FUNC_NAMES.get(id) != null;
				this.f_idx = org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES[id];
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["sprintf"])
				{
					if (this.ast1 == null)
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "sprintf requires at least 1 argument"
							);
					}
					int ast1_result = this.ast1.PopulateTuples(tuples);
					if (ast1_result == 0)
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "sprintf requires at minimum 1 argument"
							);
					}
					tuples.Sprintf(ast1_result);
					this.PopSourceLineNumber(tuples);
					return 1;
				}
				else
				{
					if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["close"])
					{
						if (this.ast1 == null)
						{
							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "close requires 1 argument"
								);
						}
						int ast1_result = this.ast1.PopulateTuples(tuples);
						if (ast1_result != 1)
						{
							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "close requires only 1 argument"
								);
						}
						tuples.Close();
						this.PopSourceLineNumber(tuples);
						return 1;
					}
					else
					{
						if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["length"])
						{
							if (this.ast1 == null)
							{
								tuples.Length(0);
							}
							else
							{
								int ast1_result = this.ast1.PopulateTuples(tuples);
								if (ast1_result != 1)
								{
									throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "length requires at least one argument"
										);
								}
								tuples.Length(1);
							}
							this.PopSourceLineNumber(tuples);
							return 1;
						}
						else
						{
							if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["srand"])
							{
								if (this.ast1 == null)
								{
									tuples.Srand(0);
								}
								else
								{
									int ast1_result = this.ast1.PopulateTuples(tuples);
									if (ast1_result != 1)
									{
										throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "srand takes either 0 or one argument, not "
											 + ast1_result);
									}
									tuples.Srand(1);
								}
								this.PopSourceLineNumber(tuples);
								return 1;
							}
							else
							{
								if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["rand"])
								{
									if (this.ast1 != null)
									{
										throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "rand does not take arguments"
											);
									}
									tuples.Rand();
									this.PopSourceLineNumber(tuples);
									return 1;
								}
								else
								{
									if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["sqrt"])
									{
										int ast1_result = this.ast1.PopulateTuples(tuples);
										if (ast1_result != 1)
										{
											throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "sqrt requires only 1 argument"
												);
										}
										tuples.Sqrt();
										this.PopSourceLineNumber(tuples);
										return 1;
									}
									else
									{
										if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["int"])
										{
											int ast1_result = this.ast1.PopulateTuples(tuples);
											if (ast1_result != 1)
											{
												throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "int requires only 1 argument"
													);
											}
											tuples.IntFunc();
											this.PopSourceLineNumber(tuples);
											return 1;
										}
										else
										{
											if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["log"])
											{
												int ast1_result = this.ast1.PopulateTuples(tuples);
												if (ast1_result != 1)
												{
													throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "int requires only 1 argument"
														);
												}
												tuples.Log();
												this.PopSourceLineNumber(tuples);
												return 1;
											}
											else
											{
												if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["exp"])
												{
													int ast1_result = this.ast1.PopulateTuples(tuples);
													if (ast1_result != 1)
													{
														throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "exp requires only 1 argument"
															);
													}
													tuples.Exp();
													this.PopSourceLineNumber(tuples);
													return 1;
												}
												else
												{
													if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["sin"])
													{
														int ast1_result = this.ast1.PopulateTuples(tuples);
														if (ast1_result != 1)
														{
															throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "sin requires only 1 argument"
																);
														}
														tuples.Sin();
														this.PopSourceLineNumber(tuples);
														return 1;
													}
													else
													{
														if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["cos"])
														{
															int ast1_result = this.ast1.PopulateTuples(tuples);
															if (ast1_result != 1)
															{
																throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "cos requires only 1 argument"
																	);
															}
															tuples.Cos();
															this.PopSourceLineNumber(tuples);
															return 1;
														}
														else
														{
															if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["atan2"])
															{
																int ast1_result = this.ast1.PopulateTuples(tuples);
																if (ast1_result != 2)
																{
																	throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "atan2 requires 2 arguments"
																		);
																}
																tuples.Atan2();
																this.PopSourceLineNumber(tuples);
																return 1;
															}
															else
															{
																if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["match"])
																{
																	int ast1_result = this.ast1.PopulateTuples(tuples);
																	if (ast1_result != 2)
																	{
																		throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "match requires 2 arguments"
																			);
																	}
																	tuples.Match();
																	this.PopSourceLineNumber(tuples);
																	return 1;
																}
																else
																{
																	if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["index"])
																	{
																		int ast1_result = this.ast1.PopulateTuples(tuples);
																		if (ast1_result != 2)
																		{
																			throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "index requires 2 arguments"
																				);
																		}
																		tuples.Index();
																		this.PopSourceLineNumber(tuples);
																		return 1;
																	}
																	else
																	{
																		if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["sub"] || this.f_idx
																			 == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["gsub"])
																		{
																			if (this.ast1 == null || this.ast1.ast2 == null || this.ast1.ast2.ast1 == null)
																			{
																				throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "sub needs at least 2 arguments"
																					);
																			}
																			bool is_gsub = this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["gsub"
																				];
																			int numargs = this.ast1.PopulateTuples(tuples);
																			// stack contains arg1,arg2[,arg3] - in that pop() order
																			if (numargs == 2)
																			{
																				tuples.SubForDollar0(is_gsub);
																			}
																			else
																			{
																				if (numargs == 3)
																				{
																					org.jawk.frontend.AwkParser.AST ptr = this.ast1.ast2.ast2.ast1;
																					if (ptr is org.jawk.frontend.AwkParser.ID_AST)
																					{
																						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)ptr;
																						if (id_ast.IsArray())
																						{
																							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "sub cannot accept an unindexed array as its 3rd argument"
																								);
																						}
																						id_ast.SetScalar(true);
																						tuples.SubForVariable(id_ast.offset, id_ast.is_global, is_gsub);
																					}
																					else
																					{
																						if (ptr is org.jawk.frontend.AwkParser.ArrayReference_AST)
																						{
																							org.jawk.frontend.AwkParser.ArrayReference_AST arr_ast = (org.jawk.frontend.AwkParser.ArrayReference_AST
																								)ptr;
																							// push the index
																							int ast2_result = arr_ast.ast2.PopulateTuples(tuples);
																							//assert ast2_result == 1;
																							org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)arr_ast
																								.ast1;
																							if (id_ast.IsScalar())
																							{
																								throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot use " +
																									 id_ast + " as an array.");
																							}
																							tuples.SubForArrayReference(id_ast.offset, id_ast.is_global, is_gsub);
																						}
																						else
																						{
																							if (ptr is org.jawk.frontend.AwkParser.DollarExpression_AST)
																							{
																								// push the field ref
																								org.jawk.frontend.AwkParser.DollarExpression_AST dollar_expr = (org.jawk.frontend.AwkParser.DollarExpression_AST
																									)ptr;
																								//assert dollar_expr.ast1 != null;
																								int ast1_result = dollar_expr.ast1.PopulateTuples(tuples);
																								//assert ast1_result == 1;
																								tuples.SubForDollarReference(is_gsub);
																							}
																							else
																							{
																								throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "sub's 3rd argument must be either an id, an array reference, or an input field reference"
																									);
																							}
																						}
																					}
																				}
																			}
																			this.PopSourceLineNumber(tuples);
																			return 1;
																		}
																		else
																		{
																			if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["split"])
																			{
																				// split can take 2 or 3 args:
																				// split (string, array [,fs])
																				// the 2nd argument is pass by reference, which is ok (?)
																				// funccallparamlist.funccallparamlist.id_ast
																				if (this.ast1 == null || this.ast1.ast2 == null || this.ast1.ast2.ast1 == null)
																				{
																					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "split needs at least 2 arguments"
																						);
																				}
																				org.jawk.frontend.AwkParser.AST ptr = this.ast1.ast2.ast1;
																				if (!(ptr is org.jawk.frontend.AwkParser.ID_AST))
																				{
																					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "split needs an array name as its 2nd argument"
																						);
																				}
																				org.jawk.frontend.AwkParser.ID_AST arr_ast = (org.jawk.frontend.AwkParser.ID_AST)
																					ptr;
																				if (arr_ast.IsScalar())
																				{
																					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "split's 2nd arg cannot be a scalar"
																						);
																				}
																				arr_ast.SetArray(true);
																				int ast1_result = this.ast1.PopulateTuples(tuples);
																				if (ast1_result != 2 && ast1_result != 3)
																				{
																					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "split requires 2 or 3 arguments, not "
																						 + ast1_result);
																				}
																				tuples.Split(ast1_result);
																				this.PopSourceLineNumber(tuples);
																				return 1;
																			}
																			else
																			{
																				if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["substr"])
																				{
																					if (this.ast1 == null)
																					{
																						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "substr requires at least 2 arguments"
																							);
																					}
																					int ast1_result = this.ast1.PopulateTuples(tuples);
																					if (ast1_result != 2 && ast1_result != 3)
																					{
																						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "substr requires 2 or 3 arguments, not "
																							 + ast1_result);
																					}
																					tuples.Substr(ast1_result);
																					this.PopSourceLineNumber(tuples);
																					return 1;
																				}
																				else
																				{
																					if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["tolower"])
																					{
																						if (this.ast1 == null)
																						{
																							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "tolower requires 1 argument"
																								);
																						}
																						int ast1_result = this.ast1.PopulateTuples(tuples);
																						if (ast1_result != 1)
																						{
																							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "tolower requires only 1 argument"
																								);
																						}
																						tuples.Tolower();
																						this.PopSourceLineNumber(tuples);
																						return 1;
																					}
																					else
																					{
																						if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["toupper"])
																						{
																							if (this.ast1 == null)
																							{
																								throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "toupper requires 1 argument"
																									);
																							}
																							int ast1_result = this.ast1.PopulateTuples(tuples);
																							if (ast1_result != 1)
																							{
																								throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "toupper requires only 1 argument"
																									);
																							}
																							tuples.Toupper();
																							this.PopSourceLineNumber(tuples);
																							return 1;
																						}
																						else
																						{
																							if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["system"])
																							{
																								if (this.ast1 == null)
																								{
																									throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "system requires 1 argument"
																										);
																								}
																								int ast1_result = this.ast1.PopulateTuples(tuples);
																								if (ast1_result != 1)
																								{
																									throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "system requires only 1 argument"
																										);
																								}
																								tuples.System();
																								this.PopSourceLineNumber(tuples);
																								return 1;
																							}
																							else
																							{
																								if (this.f_idx == org.jawk.frontend.AwkParser.BUILTIN_FUNC_NAMES["exec"])
																								{
																									if (this.ast1 == null)
																									{
																										throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "exec requires 1 argument"
																											);
																									}
																									int ast1_result = this.ast1.PopulateTuples(tuples);
																									if (ast1_result != 1)
																									{
																										throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "exec requires only 1 argument"
																											);
																									}
																									tuples.Exec();
																									this.PopSourceLineNumber(tuples);
																									return 1;
																								}
																								else
																								{
																									throw new org.jawk.NotImplementedError("builtin: " + this.id);
																								}
																							}
																						}
																					}
																				}
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			private readonly AwkParser _enclosing;
		}

		private class FunctionCallParamList_AST : org.jawk.frontend.AwkParser.AST
		{
			public FunctionCallParamList_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 expr, org.jawk.frontend.AwkParser.AST rest) : base(_enclosing,expr, rest)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				int retval;
				if (this.ast2 == null)
				{
					retval = this.ast1.PopulateTuples(tuples);
				}
				else
				{
					retval = this.ast2.PopulateTuples(tuples) + this.ast1.PopulateTuples(tuples);
				}
				this.PopSourceLineNumber(tuples);
				return retval;
			}

			private readonly AwkParser _enclosing;
		}

		private class FunctionDefParamList_AST : org.jawk.frontend.AwkParser.AST
		{
			public string id;

			public int offset;

			public FunctionDefParamList_AST(AwkParser _enclosing, string id, int offset, org.jawk.frontend.AwkParser.AST
                 rest)
                : base(_enclosing, rest)
			{
				this._enclosing = _enclosing;
				this.id = id;
				this.offset = offset;
			}

			public sealed override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				throw new System.Exception("Cannot 'execute' function definition parameter list (formal parameters) in this manner."
					);
			}

			/// <summary>
			/// According to the spec
			/// (http://www.opengroup.org/onlinepubs/007908799/xcu/awk.html)
			/// formal function parameters cannot be special variables,
			/// such as NF, NR, etc).
			/// </summary>
			/// <remarks>
			/// According to the spec
			/// (http://www.opengroup.org/onlinepubs/007908799/xcu/awk.html)
			/// formal function parameters cannot be special variables,
			/// such as NF, NR, etc).
			/// </remarks>
			/// <exception cref="SemanticException">upon a semantic error.</exception>
			/// <exception cref="org.jawk.frontend.AwkParser.AST.SemanticException"></exception>
			public override void SemanticAnalysis()
			{
				// could do it recursively, but not necessary
				// since all ast1's are FunctionDefParamList's
				// and, thus, terminals (no need to do further
				// semantic analysis)
				org.jawk.frontend.AwkParser.FunctionDefParamList_AST ptr = this;
				while (ptr != null)
				{
					if (org.jawk.frontend.AwkParser.SPECIAL_VAR_NAMES.ContainsKey(ptr.id))
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Special variable "
							 + ptr.id + " cannot be used as a formal parameter");
					}
					ptr = (org.jawk.frontend.AwkParser.FunctionDefParamList_AST)ptr.ast1;
				}
			}

			private readonly AwkParser _enclosing;
		}

		/// <summary>A tag interface for non-statement expressions.</summary>
		/// <remarks>
		/// A tag interface for non-statement expressions.
		/// Unknown for certain, but I think this is done
		/// to avoid partial variable assignment mistakes.
		/// For example, instead of a=3, the programmer
		/// inadvertently places the a on the line.  If ID_ASTs
		/// were not tagged with NonStatement_AST, then the
		/// incomplete assignment would parse properly, and
		/// the developer might remain unaware of this issue.
		/// </remarks>
		private interface INonStatement_AST
		{
		}

		private class ID_AST : org.jawk.frontend.AwkParser.AST, org.jawk.frontend.AwkParser.INonStatement_AST
		{
			public string id;

			public int offset = org.jawk.backend.AVM.NULL_OFFSET;

			public bool is_global;

			public ID_AST(AwkParser _enclosing, string id, bool is_global):base(_enclosing)
			{
				this._enclosing = _enclosing;
				this.id = id;
				this.is_global = is_global;
			}

			public bool is_array = false;

			public bool is_scalar = false;

			private System.Collections.Generic.ICollection<org.jawk.frontend.AwkParser.ID_AST> formal_parameters = new List<ID_AST>();

			public override string ToString()
			{
				return base.ToString() + " (" + this.id + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert offset != AVM.NULL_OFFSET : "offset = "+offset+" for "+this;
				tuples.Dereference(this.offset, this.IsArray(), this.is_global);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			public sealed override bool IsArray()
			{
				return this.is_array;
			}

			public sealed override bool IsScalar()
			{
				return this.is_scalar;
			}

			public void SetArray(bool b)
			{
				this.is_array = b;
			}

			public void SetScalar(bool b)
			{
				this.is_scalar = b;
			}

			private readonly AwkParser _enclosing;
		}

		private class ArrayReference_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public ArrayReference_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                id_ast, org.jawk.frontend.AwkParser.AST idx_ast)
                : base(_enclosing, id_ast, idx_ast)
			{
				this._enclosing = _enclosing;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.ast1 + " [...])";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				//assert ast2 != null;
				// get the index
				int ast2_result = this.ast2.PopulateTuples(tuples);
				//assert ast2_result == 1;
				// get the array var
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.DereferenceArray();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class Integer_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST, org.jawk.frontend.AwkParser.INonStatement_AST
		{
			private int I;

			public Integer_AST(AwkParser _enclosing, int I):base(_enclosing)
			{
				this._enclosing = _enclosing;
				this.I = I;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.I + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				tuples.Push(this.I);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		/// <summary>
		/// Can either assume the role of a double or an integer
		/// by aggressively normalizing the value to an int if possible.
		/// </summary>
		/// <remarks>
		/// Can either assume the role of a double or an integer
		/// by aggressively normalizing the value to an int if possible.
		/// </remarks>
		private class Double_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST, org.jawk.frontend.AwkParser.INonStatement_AST
		{
			private object D;

			public Double_AST(AwkParser _enclosing, double D):base(_enclosing)
			{
				this._enclosing = _enclosing;
				double d = D;
				if (d == (int)d)
				{
					this.D = (int)d;
				}
				else
				{
					this.D = d;
				}
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.D + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				tuples.Push(this.D);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		/// <summary>
		/// A string is a string; Awk doesn't attempt to normalize
		/// it until it is used in an arithmetic operation!
		/// </summary>
		private class String_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST, org.jawk.frontend.AwkParser.INonStatement_AST
		{
			private string S;

			public String_AST(AwkParser _enclosing, string str):base(_enclosing)
			{
				this._enclosing = _enclosing;
				//assert str != null;
				this.S = str;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.S + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				tuples.Push(this.S);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class Regexp_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			private string regexp_str;

			public Regexp_AST(AwkParser _enclosing, string regexp_str):base(_enclosing)
			{
				this._enclosing = _enclosing;
				//assert regexp_str != null;
				this.regexp_str = regexp_str;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.regexp_str + ")";
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				tuples.Regexp(this.regexp_str);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class RegexpPair_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public RegexpPair_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST regexp_ast_1
				, org.jawk.frontend.AwkParser.AST regexp_ast_2) : base(_enclosing,regexp_ast_1, regexp_ast_2
				)
			{
				this._enclosing = _enclosing;
			}

			// private class Regexp_AST {AST}
			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast2 != null;
				int ast2_result = this.ast2.PopulateTuples(tuples);
				//assert ast2_result == 1;
				//assert ast1 != null;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.RegexpPair();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class IntegerExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public IntegerExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 expr) : base(_enclosing,expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.CastInt();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class DoubleExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public DoubleExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 expr) : base(_enclosing,expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.CastDouble();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class StringExpression_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public StringExpression_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
				 expr) : base(_enclosing,expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				tuples.CastString();
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class Begin_AST : org.jawk.frontend.AwkParser.AST
		{
            public Begin_AST(AwkParser _enclosing)
                : base(_enclosing)
			{
				this._enclosing = _enclosing;
				this.is_begin = true;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				tuples.Push(1);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class End_AST : org.jawk.frontend.AwkParser.AST
		{
            public End_AST(AwkParser _enclosing)
                : base(_enclosing)
			{
				this._enclosing = _enclosing;
				this.is_end = true;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				tuples.Push(1);
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class PreInc_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public PreInc_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST symbol_ast
				) : base(_enclosing,symbol_ast)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				if (this.ast1 is org.jawk.frontend.AwkParser.ID_AST)
				{
					org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
						.ast1;
					tuples.Inc(id_ast.offset, id_ast.is_global);
				}
				else
				{
					if (this.ast1 is org.jawk.frontend.AwkParser.ArrayReference_AST)
					{
						org.jawk.frontend.AwkParser.ArrayReference_AST arr_ast = (org.jawk.frontend.AwkParser.ArrayReference_AST
							)this.ast1;
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)arr_ast
							.ast1;
						//assert id_ast != null;
						//assert arr_ast.ast2 != null;
						int arr_ast2_result = arr_ast.ast2.PopulateTuples(tuples);
						//assert arr_ast2_result == 1;
						tuples.IncArrayRef(id_ast.offset, id_ast.is_global);
					}
					else
					{
						if (this.ast1 is org.jawk.frontend.AwkParser.DollarExpression_AST)
						{
							org.jawk.frontend.AwkParser.DollarExpression_AST dollar_expr = (org.jawk.frontend.AwkParser.DollarExpression_AST
								)this.ast1;
							//assert dollar_expr.ast1 != null;
							int lAst1_result = dollar_expr.ast1.PopulateTuples(tuples);
							//assert ast1_result == 1;
							// OPTIMIATION: duplicate the x in $x here
							// so that it is not evaluated again
							tuples.Dup();
							// stack contains eval of dollar arg
							//tuples.assignAsInputField();
							tuples.IncDollarRef();
							// OPTIMIATION continued: now evaluate
							// the dollar expression with x (for $x)
							// instead of evaluating the expression again
							tuples.GetInputField();
							this.PopSourceLineNumber(tuples);
							return 1;
						}
						else
						{
							// NOTE, short-circuit return here!
							throw new org.jawk.NotImplementedError("unhandled preinc for " + this.ast1);
						}
					}
				}
				//else
				//	//assert false : "cannot refer for pre_inc to "+ast1;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class PreDec_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public PreDec_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST symbol_ast
                )
                : base(_enclosing, symbol_ast)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				if (this.ast1 is org.jawk.frontend.AwkParser.ID_AST)
				{
					org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
						.ast1;
					tuples.Dec(id_ast.offset, id_ast.is_global);
				}
				else
				{
					if (this.ast1 is org.jawk.frontend.AwkParser.ArrayReference_AST)
					{
						org.jawk.frontend.AwkParser.ArrayReference_AST arr_ast = (org.jawk.frontend.AwkParser.ArrayReference_AST
							)this.ast1;
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)arr_ast
							.ast1;
						//assert id_ast != null;
						//assert arr_ast.ast2 != null;
						int arr_ast2_result = arr_ast.ast2.PopulateTuples(tuples);
						//assert arr_ast2_result == 1;
						tuples.DecArrayRef(id_ast.offset, id_ast.is_global);
					}
					else
					{
						if (this.ast1 is org.jawk.frontend.AwkParser.DollarExpression_AST)
						{
							org.jawk.frontend.AwkParser.DollarExpression_AST dollar_expr = (org.jawk.frontend.AwkParser.DollarExpression_AST
								)this.ast1;
							//assert dollar_expr.ast1 != null;
							int lAst1_result = dollar_expr.ast1.PopulateTuples(tuples);
							//assert ast1_result == 1;
							// OPTIMIATION: duplicate the x in $x here
							// so that it is not evaluated again
							tuples.Dup();
							// stack contains eval of dollar arg
							//tuples.assignAsInputField();
							tuples.DecDollarRef();
							// OPTIMIATION continued: now evaluate
							// the dollar expression with x (for $x)
							// instead of evaluating the expression again
							tuples.GetInputField();
							this.PopSourceLineNumber(tuples);
							return 1;
						}
						else
						{
							// NOTE, short-circuit return here!
							throw new org.jawk.NotImplementedError("unhandled predec for " + this.ast1);
						}
					}
				}
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class PostInc_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public PostInc_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST symbol_ast
                )
                : base(_enclosing, symbol_ast)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				if (this.ast1 is org.jawk.frontend.AwkParser.ID_AST)
				{
					org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
						.ast1;
					tuples.Inc(id_ast.offset, id_ast.is_global);
				}
				else
				{
					if (this.ast1 is org.jawk.frontend.AwkParser.ArrayReference_AST)
					{
						org.jawk.frontend.AwkParser.ArrayReference_AST arr_ast = (org.jawk.frontend.AwkParser.ArrayReference_AST
							)this.ast1;
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)arr_ast
							.ast1;
						//assert id_ast != null;
						//assert arr_ast.ast2 != null;
						int arr_ast2_result = arr_ast.ast2.PopulateTuples(tuples);
						//assert arr_ast2_result == 1;
						tuples.IncArrayRef(id_ast.offset, id_ast.is_global);
					}
					else
					{
						if (this.ast1 is org.jawk.frontend.AwkParser.DollarExpression_AST)
						{
							org.jawk.frontend.AwkParser.DollarExpression_AST dollar_expr = (org.jawk.frontend.AwkParser.DollarExpression_AST
								)this.ast1;
							//assert dollar_expr.ast1 != null;
							int dollarast_ast1_result = dollar_expr.ast1.PopulateTuples(tuples);
							//assert dollarast_ast1_result == 1;
							tuples.IncDollarRef();
						}
						else
						{
							throw new org.jawk.NotImplementedError("unhandled postinc for " + this.ast1);
						}
					}
				}
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class PostDec_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public PostDec_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST symbol_ast
                )
                : base(_enclosing, symbol_ast)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				int ast1_result = this.ast1.PopulateTuples(tuples);
				//assert ast1_result == 1;
				if (this.ast1 is org.jawk.frontend.AwkParser.ID_AST)
				{
					org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
						.ast1;
					tuples.Dec(id_ast.offset, id_ast.is_global);
				}
				else
				{
					if (this.ast1 is org.jawk.frontend.AwkParser.ArrayReference_AST)
					{
						org.jawk.frontend.AwkParser.ArrayReference_AST arr_ast = (org.jawk.frontend.AwkParser.ArrayReference_AST
							)this.ast1;
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)arr_ast
							.ast1;
						//assert id_ast != null;
						//assert arr_ast.ast2 != null;
						int arr_ast2_result = arr_ast.ast2.PopulateTuples(tuples);
						//assert arr_ast2_result == 1;
						tuples.DecArrayRef(id_ast.offset, id_ast.is_global);
					}
					else
					{
						if (this.ast1 is org.jawk.frontend.AwkParser.DollarExpression_AST)
						{
							org.jawk.frontend.AwkParser.DollarExpression_AST dollar_expr = (org.jawk.frontend.AwkParser.DollarExpression_AST
								)this.ast1;
							//assert dollar_expr.ast1 != null;
							int dollarast_ast1_result = dollar_expr.ast1.PopulateTuples(tuples);
							//assert dollarast_ast1_result == 1;
							tuples.DecDollarRef();
						}
						else
						{
							throw new org.jawk.NotImplementedError("unhandled postinc for " + this.ast1);
						}
					}
				}
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class Print_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			private int output_token;

			public Print_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr_list
                , int output_token, org.jawk.frontend.AwkParser.AST output_expr)
                : base(_enclosing, expr_list
				, output_expr)
			{
				this._enclosing = _enclosing;
				this.output_token = output_token;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int param_count;
				if (this.ast1 == null)
				{
					param_count = 0;
				}
				else
				{
					param_count = this.ast1.PopulateTuples(tuples);
					//assert param_count >= 0;
					if (param_count == 0)
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot print the result.  The expression doesn't return anything."
							);
					}
				}
				if (this.ast2 != null)
				{
					int ast2_result = this.ast2.PopulateTuples(tuples);
				}
				//assert ast2_result == 1;
				if (this.output_token == org.jawk.frontend.AwkParser._GT_)
				{
					tuples.PrintToFile(param_count, false);
				}
				else
				{
					// false = no append
					if (this.output_token == org.jawk.frontend.AwkParser._APPEND_)
					{
						tuples.PrintToFile(param_count, true);
					}
					else
					{
						// false = no append
						if (this.output_token == org.jawk.frontend.AwkParser._PIPE_)
						{
							tuples.PrintToPipe(param_count);
						}
						else
						{
							tuples.Print(param_count);
						}
					}
				}
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class Extension_AST : org.jawk.frontend.AwkParser.AST
		{
			private string extension_keyword;

			public Extension_AST(AwkParser _enclosing, string extension_keyword, org.jawk.frontend.AwkParser.AST
                 param_ast)
                : base(_enclosing, param_ast)
			{
				this._enclosing = _enclosing;
				// we don't know if it is a scalar
				this.extension_keyword = extension_keyword;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int param_count;
				if (this.ast1 == null)
				{
					param_count = 0;
				}
				else
				{
					/// Query for the extension.
					org.jawk.ext.IJawkExtension extension = this._enclosing.extensions[this.extension_keyword
						];
					int arg_count = this.CountParams((org.jawk.frontend.AwkParser.FunctionCallParamList_AST
						)this.ast1);
					/// Get all required assoc array parameters:
					int[] req_array_idxs = extension.GetAssocArrayParameterPositions(this.extension_keyword
						, arg_count);
					//assert req_array_idxs != null;
					foreach (int idx in req_array_idxs)
					{
						org.jawk.frontend.AwkParser.AST param_ast = this.GetParamAst((org.jawk.frontend.AwkParser.FunctionCallParamList_AST
							)this.ast1, idx);
						//assert ast1 instanceof FunctionCallParamList_AST;
						// if the parameter is an ID_AST...
						if (param_ast.ast1 is org.jawk.frontend.AwkParser.ID_AST)
						{
							// then force it to be an array,
							// or complain if it is already tagged as a scalar
							org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)param_ast
								.ast1;
							if (id_ast.IsScalar())
							{
								throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Extension '" +
									 this.extension_keyword + "' requires parameter position " + idx + " be an associative array, not a scalar."
									);
							}
							id_ast.SetArray(true);
						}
					}
					param_count = this.ast1.PopulateTuples(tuples);
				}
				//assert param_count >= 0;
				// is_initial == true ::
				// retval of this extension is not a function parameter
				// of another extension
				// true iff Extension | FunctionCallParam | FunctionCallParam | etc.
				bool is_initial;
				if (this.parent is org.jawk.frontend.AwkParser.FunctionCallParamList_AST)
				{
					org.jawk.frontend.AwkParser.AST ptr = this.parent;
					while (ptr is org.jawk.frontend.AwkParser.FunctionCallParamList_AST)
					{
						ptr = ptr.parent;
					}
					is_initial = !(ptr is org.jawk.frontend.AwkParser.Extension_AST);
				}
				else
				{
					is_initial = true;
				}
				tuples.Extension(this.extension_keyword, param_count, is_initial);
				this.PopSourceLineNumber(tuples);
				// an extension always returns a value, even if it is blank/null
				return 1;
			}

			private org.jawk.frontend.AwkParser.AST GetParamAst(org.jawk.frontend.AwkParser.FunctionCallParamList_AST
				 p_ast, int pos)
			{
				for (int i = 0; i < pos; ++i)
				{
					p_ast = (org.jawk.frontend.AwkParser.FunctionCallParamList_AST)p_ast.ast2;
					if (p_ast == null)
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "More arguments required for assoc array parameter position specification."
							);
					}
				}
				return p_ast;
			}

			private int CountParams(org.jawk.frontend.AwkParser.FunctionCallParamList_AST p_ast
				)
			{
				int cnt = 0;
				while (p_ast != null)
				{
					p_ast = (org.jawk.frontend.AwkParser.FunctionCallParamList_AST)p_ast.ast2;
					++cnt;
				}
				return cnt;
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.extension_keyword + ")";
			}

			private readonly AwkParser _enclosing;
		}

		private class Printf_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			private int output_token;

			public Printf_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr_list
                , int output_token, org.jawk.frontend.AwkParser.AST output_expr)
                : base(_enclosing, expr_list
				, output_expr)
			{
				this._enclosing = _enclosing;
				this.output_token = output_token;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				int param_count;
				if (this.ast1 == null)
				{
					param_count = 0;
				}
				else
				{
					param_count = this.ast1.PopulateTuples(tuples);
					//assert param_count >= 0;
					if (param_count == 0)
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot printf the result.  The expression doesn't return anything."
							);
					}
				}
				if (this.ast2 != null)
				{
					int ast2_result = this.ast2.PopulateTuples(tuples);
				}
				//assert ast2_result == 1;
				if (this.output_token == org.jawk.frontend.AwkParser._GT_)
				{
					tuples.PrintfToFile(param_count, false);
				}
				else
				{
					// false = no append
					if (this.output_token == org.jawk.frontend.AwkParser._APPEND_)
					{
						tuples.PrintfToFile(param_count, true);
					}
					else
					{
						// false = no append
						if (this.output_token == org.jawk.frontend.AwkParser._PIPE_)
						{
							tuples.PrintfToPipe(param_count);
						}
						else
						{
							tuples.Printf(param_count);
						}
					}
				}
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class Getline_AST : org.jawk.frontend.AwkParser.ScalarExpression_AST
		{
			public Getline_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST pipe_expr
				, org.jawk.frontend.AwkParser.AST lvalue_ast, org.jawk.frontend.AwkParser.AST in_redirect
                )
                : base(_enclosing, pipe_expr, lvalue_ast, in_redirect)
			{
				this._enclosing = _enclosing;
				if (this._enclosing.no_input && pipe_expr == null && in_redirect == null)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "getline via stdin/ARGV disabled by the -ni option"
						);
				}
			}

			// cannot have both pipe_expr and in_redirect NOT null!
			//assert pipe_expr == null || in_redirect == null;
			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				if (this.ast1 != null)
				{
					int ast1_result = this.ast1.PopulateTuples(tuples);
					//assert ast1_result == 1;
					// stack has ast1 (i.e., "command")
					tuples.UseAsCommandInput();
				}
				else
				{
					if (this.ast3 != null)
					{
						// getline ... < ast3
						int ast3_result = this.ast3.PopulateTuples(tuples);
						//assert ast3_result == 1;
						// stack has ast3 (i.e., "filename")
						tuples.UseAsFileInput();
					}
					else
					{
						tuples.GetlineInput();
					}
				}
				// 2 resultant values on the stack!
				// 2nd - -1/0/1 for io-err,eof,success
				// 1st(top) - the input
				if (this.ast2 == null)
				{
					tuples.AssignAsInput();
				}
				else
				{
					// stack still has the input, to be popped below...
					// (all assignment results are placed on the stack)
					if (this.ast2 is org.jawk.frontend.AwkParser.ID_AST)
					{
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
							.ast2;
						tuples.Assign(id_ast.offset, id_ast.is_global);
						if (id_ast.id.Equals("RS"))
						{
							tuples.ApplyRS();
						}
					}
					else
					{
						if (this.ast2 is org.jawk.frontend.AwkParser.ArrayReference_AST)
						{
							org.jawk.frontend.AwkParser.ArrayReference_AST arr = (org.jawk.frontend.AwkParser.ArrayReference_AST
								)this.ast2;
							// push the index
							//assert arr.ast2 != null;
							int arr_ast2_result = arr.ast2.PopulateTuples(tuples);
							//assert arr_ast2_result == 1;
							// push the array ref itself
							org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)arr
								.ast1;
							tuples.AssignArray(id_ast.offset, id_ast.is_global);
						}
						else
						{
							if (this.ast2 is org.jawk.frontend.AwkParser.DollarExpression_AST)
							{
								org.jawk.frontend.AwkParser.DollarExpression_AST dollar_expr = (org.jawk.frontend.AwkParser.DollarExpression_AST
									)this.ast2;
								//assert dollar_expr.ast2 != null;
								int ast2_result = dollar_expr.ast2.PopulateTuples(tuples);
								//assert ast2_result == 1;
								// stack contains eval of dollar arg
								tuples.AssignAsInputField();
							}
							else
							{
								throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot getline into a "
									 + this.ast2);
							}
						}
					}
				}
				// get rid of value left by the assignment
				tuples.Pop();
				// one value is left on the stack
				this.PopSourceLineNumber(tuples);
				return 1;
			}

			private readonly AwkParser _enclosing;
		}

		private class ReturnStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public ReturnStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                 expr)
                : base(_enclosing, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				org.jawk.frontend.AwkParser.IReturnable returnable = (org.jawk.frontend.AwkParser.IReturnable
					)this.SearchFor(typeof(org.jawk.frontend.AwkParser.IReturnable));
				if (returnable == null)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "Cannot use return here."
						);
				}
				if (this.ast1 != null)
				{
					int ast1_result = this.ast1.PopulateTuples(tuples);
					//assert ast1_result == 1;
					tuples.SetReturnResult();
				}
				tuples.GotoAddress(returnable.ReturnAddress());
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class ExitStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public ExitStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr
                )
                : base(_enclosing, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				if (this.ast1 != null)
				{
					int ast1_result = this.ast1.PopulateTuples(tuples);
				}
				else
				{
					//assert ast1_result == 1;
					tuples.Push(0);
				}
				tuples.ExitWithCode();
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class DeleteStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public DeleteStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                 symbol_ast)
                : base(_enclosing, symbol_ast)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				//assert ast1 != null;
				if (this.ast1 is org.jawk.frontend.AwkParser.ArrayReference_AST)
				{
					//assert ast1.ast1 != null;	// a in a[b]
					//assert ast1.ast2 != null;	// b in a[b]
					org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
						.ast1.ast1;
					if (id_ast.IsScalar())
					{
						throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "delete: Cannot use a scalar as an array."
							);
					}
					id_ast.SetArray(true);
					int idx_result = this.ast1.ast2.PopulateTuples(tuples);
					//assert idx_result == 1;
					// idx on the stack
					tuples.DeleteArrayElement(id_ast.offset, id_ast.is_global);
				}
				else
				{
					if (this.ast1 is org.jawk.frontend.AwkParser.ID_AST)
					{
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)this
							.ast1;
						if (id_ast.IsScalar())
						{
							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "delete: Cannot delete a scalar."
								);
						}
						id_ast.SetArray(true);
						tuples.DeleteArray(id_ast.offset, id_ast.is_global);
					}
					else
					{
						throw new System.Exception("Should never reach here : delete for " + this.ast1);
					}
				}
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class BreakStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				org.jawk.frontend.AwkParser.IBreakable breakable = (org.jawk.frontend.AwkParser.IBreakable
					)this.SearchFor(typeof(org.jawk.frontend.AwkParser.IBreakable));
				if (breakable == null)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "cannot break; not within a loop"
						);
				}
				//assert breakable != null;
				tuples.GotoAddress(breakable.BreakAddress());
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			internal BreakStatement_AST(AwkParser _enclosing):base(_enclosing)
			{
				this._enclosing = _enclosing;
			}

			private readonly AwkParser _enclosing;
		}

		private class SleepStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public SleepStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST
                expr)
                : base(_enclosing, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				if (this.ast1 == null)
				{
					tuples.Sleep(0);
				}
				else
				{
					int ast1_result = this.ast1.PopulateTuples(tuples);
					//assert ast1_result == 1;
					tuples.Sleep(ast1_result);
				}
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class DumpStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public DumpStatement_AST(AwkParser _enclosing, org.jawk.frontend.AwkParser.AST expr
                )
                : base(_enclosing, expr)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				if (this.ast1 == null)
				{
					tuples.Dump(0);
				}
				else
				{
					//assert ast1 instanceof FunctionCallParamList_AST;
					org.jawk.frontend.AwkParser.AST ptr = this.ast1;
					while (ptr != null)
					{
						if (!(ptr.ast1 is org.jawk.frontend.AwkParser.ID_AST))
						{
							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "ID required for argument(s) to _dump"
								);
						}
						org.jawk.frontend.AwkParser.ID_AST id_ast = (org.jawk.frontend.AwkParser.ID_AST)ptr
							.ast1;
						if (id_ast.IsScalar())
						{
							throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "_dump: Cannot use a scalar as an argument."
								);
						}
						id_ast.SetArray(true);
						ptr = ptr.ast2;
					}
					int ast1_result = this.ast1.PopulateTuples(tuples);
					tuples.Dump(ast1_result);
				}
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class NextStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				org.jawk.frontend.AwkParser.INextable nextable = (org.jawk.frontend.AwkParser.INextable
					)this.SearchFor(typeof(org.jawk.frontend.AwkParser.INextable));
				if (nextable == null)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "cannot next; not within any input rules"
						);
				}
				//assert nextable != null;
				tuples.GotoAddress(nextable.NextAddress());
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			internal NextStatement_AST(AwkParser _enclosing):base(_enclosing)
			{
				this._enclosing = _enclosing;
			}

			private readonly AwkParser _enclosing;
		}

		private class ContinueStatement_AST : org.jawk.frontend.AwkParser.AST
		{
			public ContinueStatement_AST(AwkParser _enclosing) : base(_enclosing)
			{
				this._enclosing = _enclosing;
			}

			public override int PopulateTuples(org.jawk.intermediate.AwkTuples tuples)
			{
				this.PushSourceLineNumber(tuples);
				org.jawk.frontend.AwkParser.IContinueable continueable = (org.jawk.frontend.AwkParser.IContinueable
					)this.SearchFor(typeof(org.jawk.frontend.AwkParser.IContinueable));
				if (continueable == null)
				{
					throw new org.jawk.frontend.AwkParser.AST.SemanticException(this, "cannot issue a continue; not within any loops"
						);
				}
				//assert continueable != null;
				tuples.GotoAddress(continueable.ContinueAddress());
				this.PopSourceLineNumber(tuples);
				return 0;
			}

			private readonly AwkParser _enclosing;
		}

		private class FunctionProxy : org.jawk.intermediate.IHasFunctionAddress
		{
			private org.jawk.frontend.AwkParser.FunctionDef_AST function_def_ast;

			private string id;

			public FunctionProxy(AwkParser _enclosing, string id)
			{
				this._enclosing = _enclosing;
				// this was static...
				// made non-static to throw a meaningful ParserException when necessary
				this.id = id;
			}

			public void SetFunctionDefinition(org.jawk.frontend.AwkParser.FunctionDef_AST function_def
				)
			{
				if (this.function_def_ast != null)
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this._enclosing, "function " + function_def
						 + " already defined");
				}
				else
				{
					this.function_def_ast = function_def;
				}
			}

			public bool IsDefined()
			{
				return this.function_def_ast != null;
			}

			public virtual org.jawk.intermediate.IAddress GetFunctionAddress()
			{
				return this.function_def_ast.GetAddress();
			}

			public string GetFunctionName()
			{
				return this.id;
			}

			public int GetFunctionParamCount()
			{
				return this.function_def_ast.ParamCount();
			}

			public override string ToString()
			{
				return base.ToString() + " (" + this.id + ")";
			}

			public void CheckActualToFormalParameters(org.jawk.frontend.AwkParser.AST actual_params
				)
			{
				this.function_def_ast.CheckActualToFormalParameters(actual_params);
			}

			private readonly AwkParser _enclosing;
		}

		// private static class FunctionProxy
		/// <summary>
		/// Adds {var_name -&gt; offset} mappings to the tuples so that global variables
		/// can be set by the interpreter while processing filename and name=value
		/// entries from the command-line.
		/// </summary>
		/// <remarks>
		/// Adds {var_name -&gt; offset} mappings to the tuples so that global variables
		/// can be set by the interpreter while processing filename and name=value
		/// entries from the command-line.
		/// Also sends function names to the tuples, to provide the back end
		/// with names to invalidate if name=value assignments are passed
		/// in via the -v or ARGV arguments.
		/// </remarks>
		/// <param name="tuples">The tuples to add the mapping to.</param>
		public virtual void PopulateGlobalVariableNameToOffsetMappings(org.jawk.intermediate.AwkTuples
			 tuples)
		{
			foreach (string varname in symbol_table.global_ids.Keys)
			{
				org.jawk.frontend.AwkParser.ID_AST id_ast = symbol_table.global_ids[varname];
				// The last arg originally was ", id_ast.is_scalar", but this is not set true
				// if the variable use is ambiguous.  Therefore, assume it is a scalar
				// if it's NOT used as an array.
				tuples.AddGlobalVariableNameToOffsetMapping(varname, id_ast.offset, id_ast.is_array
					);
			}
			tuples.SetFunctionNameSet(symbol_table.function_proxies.Keys);
		}

		private class AwkSymbolTableImpl
		{
			internal virtual int NumGlobals()
			{
				return this.global_ids.Count;
			}

			private org.jawk.frontend.AwkParser.Begin_AST begin_ast = null;

			private org.jawk.frontend.AwkParser.End_AST end_ast = null;

			public System.Collections.Generic.IDictionary<string, org.jawk.frontend.AwkParser.FunctionProxy
				> function_proxies = new System.Collections.Generic.Dictionary<string, org.jawk.frontend.AwkParser.FunctionProxy
				>();

			public System.Collections.Generic.IDictionary<string, org.jawk.frontend.AwkParser.ID_AST
				> global_ids = new System.Collections.Generic.Dictionary<string, org.jawk.frontend.AwkParser.ID_AST
				>();

			private System.Collections.Generic.IDictionary<string, System.Collections.Generic.IDictionary
				<string, org.jawk.frontend.AwkParser.ID_AST>> local_ids = new System.Collections.Generic.Dictionary
				<string, System.Collections.Generic.IDictionary<string, org.jawk.frontend.AwkParser.ID_AST
				>>();

			private System.Collections.Generic.IDictionary<string, System.Collections.Generic.ICollection
				<string>> function_parameters = new System.Collections.Generic.Dictionary<string
				, System.Collections.Generic.ICollection<string>>();

			private System.Collections.Generic.ICollection<string> ids = new List<string>();

			private string func_name = null;

			// "constants"
			// functions (proxies)
			// variable management
			// current function definition for symbols
			// using set/clear rather than push/pop, it is impossible to define functions within functions
			internal virtual void SetFunctionName(string func_name)
			{
				//assert this.func_name == null;
				this.func_name = func_name;
			}

			internal virtual void ClearFunctionName(string func_name)
			{
				//assert this.func_name != null && this.func_name.length() > 0;
				//assert this.func_name.equals(func_name);
				this.func_name = null;
			}

			internal virtual org.jawk.frontend.AwkParser.AST AddBEGIN()
			{
				if (this.begin_ast == null)
				{
					this.begin_ast = new org.jawk.frontend.AwkParser.Begin_AST(this._enclosing);
				}
				return this.begin_ast;
			}

			internal virtual org.jawk.frontend.AwkParser.AST AddEND()
			{
				if (this.end_ast == null)
				{
					this.end_ast = new org.jawk.frontend.AwkParser.End_AST(this._enclosing);
				}
				return this.end_ast;
			}

			public org.jawk.frontend.AwkParser.ID_AST GetID(string id)
			{
				if (this.function_proxies.ContainsKey(id))
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this._enclosing, "cannot use " + id + 
						" as a variable; it is a function");
				}
				// put in the pool of ids to guard against using it as a function name
				this.ids.Add(id);
				System.Collections.Generic.IDictionary<string, org.jawk.frontend.AwkParser.ID_AST
					> map;
				if (this.func_name == null)
				{
					map = this.global_ids;
				}
				else
				{
					System.Collections.Generic.ICollection<string> set = null;
                    if(this.function_parameters.ContainsKey(this.func_name))
                    {
                        set = this.function_parameters[this.func_name];
                    }
					// we need "set != null && ..." here because if function
					// is defined with no args (i.e., function f() ...),
					// then set is null
					if (set != null && set.Contains(id))
					{
						map = null;
                        if(this.local_ids.ContainsKey(this.func_name))
                        {
                            map = this.local_ids[this.func_name];
                        }
						if (map == null)
						{
							this.local_ids[this.func_name] = map = new System.Collections.Generic.Dictionary<
								string, org.jawk.frontend.AwkParser.ID_AST>();
						}
					}
					else
					{
						map = this.global_ids;
					}
				}
				//assert map != null;
                org.jawk.frontend.AwkParser.ID_AST id_ast = null;
                if (map.ContainsKey(id))
                {
                    id_ast = map[id];
                }
			    if (id_ast == null)
				{
					id_ast = new org.jawk.frontend.AwkParser.ID_AST(this._enclosing, id, map == this.global_ids);
					id_ast.offset = map.Count;
					//assert id_ast.offset != AVM.NULL_OFFSET;
					map[id] = id_ast;
				}
				return id_ast;
			}

			/// <exception cref="org.jawk.frontend.AwkParser.ParserException"></exception>
			internal virtual org.jawk.frontend.AwkParser.AST AddID(string id)
			{
				org.jawk.frontend.AwkParser.ID_AST ret_val = this.GetID(id);
				// ***
				// We really don't know if the evaluation is for an array or for a scalar
				// here, because we can use an array as a function parameter (passed by reference).
				// ***
				//if (ret_val.is_array)
				//	throw new ParserException("Cannot use "+ret_val+" as a scalar.");
				//ret_val.is_scalar = true;
				return ret_val;
			}

			internal virtual int AddFunctionParameter(string func_name, string id)
			{
				System.Collections.Generic.ICollection<string> set = null;
                if(this.function_parameters.ContainsKey(func_name))
                {
                    set = this.function_parameters[func_name];
                }

				if (set == null)
				{
					this.function_parameters[func_name] = set = new List<string>();
				}
				if (set.Contains(id))
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this._enclosing, "multiply defined parameter "
						 + id + " in function " + func_name);
				}
				int retval = set.Count;
				set.Add(id);
				System.Collections.Generic.IDictionary<string, org.jawk.frontend.AwkParser.ID_AST> map = null;
                if(this.local_ids.ContainsKey(func_name))
                {
                    map = this.local_ids[func_name];
                }
				if (map == null)
				{
					this.local_ids[func_name] = map = new System.Collections.Generic.Dictionary<string
						, org.jawk.frontend.AwkParser.ID_AST>();
				}
				//assert map != null;
				org.jawk.frontend.AwkParser.ID_AST id_ast = null;
                if(map.ContainsKey(id))
                {
                    id_ast = map[id];   
                }
				if (id_ast == null)
				{
                    id_ast = new org.jawk.frontend.AwkParser.ID_AST(this._enclosing, id, map == this.global_ids);
					id_ast.offset = map.Count;
					//assert id_ast.offset != AVM.NULL_OFFSET;
					map[id] = id_ast;
				}
				return retval;
			}

			internal virtual org.jawk.frontend.AwkParser.ID_AST GetFunctionParameterIDAST(string
				 func_name, string f_id_string)
			{
				return this.local_ids[func_name][f_id_string];
			}

			/// <exception cref="org.jawk.frontend.AwkParser.ParserException"></exception>
			internal virtual org.jawk.frontend.AwkParser.AST AddArrayID(string id)
			{
				org.jawk.frontend.AwkParser.ID_AST ret_val = this.GetID(id);
				if (ret_val.IsScalar())
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this._enclosing, "Cannot use " + ret_val
						 + " as an array.");
				}
				ret_val.SetArray(true);
				return ret_val;
			}

			internal virtual org.jawk.frontend.AwkParser.AST AddFunctionDef(string func_name, 
				org.jawk.frontend.AwkParser.AST param_list, org.jawk.frontend.AwkParser.AST block
				)
			{
				if (this.ids.Contains(func_name))
				{
					throw new org.jawk.frontend.AwkParser.ParserException(this._enclosing, "cannot use " + func_name
						 + " as a function; it is a variable");
				}
				org.jawk.frontend.AwkParser.FunctionProxy function_proxy = null;
                if(this.function_proxies.ContainsKey(func_name))
                {
                    function_proxy = this.function_proxies[func_name];
                }
				if (function_proxy == null)
				{
					this.function_proxies[func_name] = function_proxy = new org.jawk.frontend.AwkParser.FunctionProxy
						(this._enclosing, func_name);
				}
				org.jawk.frontend.AwkParser.FunctionDef_AST function_def = new org.jawk.frontend.AwkParser.FunctionDef_AST
					(this._enclosing, func_name, param_list, block);
				function_proxy.SetFunctionDefinition(function_def);
				return function_def;
			}

			internal virtual org.jawk.frontend.AwkParser.AST AddFunctionCall(string id, org.jawk.frontend.AwkParser.AST
				 param_list)
			{
                org.jawk.frontend.AwkParser.FunctionProxy function_proxy = null;
                if (this.function_proxies.ContainsKey(id))
                {
                    function_proxy = this.function_proxies[id];
                }
				if (function_proxy == null)
				{
					this.function_proxies[id] = function_proxy = new org.jawk.frontend.AwkParser.FunctionProxy
						(this._enclosing, id);
				}
				return new org.jawk.frontend.AwkParser.FunctionCall_AST(this._enclosing, function_proxy, param_list
					);
			}

			/// <exception cref="org.jawk.frontend.AwkParser.ParserException"></exception>
			internal virtual org.jawk.frontend.AwkParser.AST AddArrayReference(string id, org.jawk.frontend.AwkParser.AST
				 idx_ast)
			{
				return new org.jawk.frontend.AwkParser.ArrayReference_AST(this._enclosing, this.AddArrayID(id
					), idx_ast);
			}

			// constants are no longer cached/hashed so that individual ASTs
			// can report accurate line numbers upon errors
			internal virtual org.jawk.frontend.AwkParser.AST AddINTEGER(string integer)
			{
				return new org.jawk.frontend.AwkParser.Integer_AST(this._enclosing, int.Parse(integer));
			}

			internal virtual org.jawk.frontend.AwkParser.AST AddDOUBLE(string dbl)
			{
                return new org.jawk.frontend.AwkParser.Double_AST(this._enclosing, double.Parse(dbl, System.Globalization.CultureInfo.InvariantCulture.NumberFormat));
			}

			internal virtual org.jawk.frontend.AwkParser.AST AddSTRING(string str)
			{
				return new org.jawk.frontend.AwkParser.String_AST(this._enclosing, str);
			}

			internal virtual org.jawk.frontend.AwkParser.AST AddREGEXP(string regexp)
			{
				return new org.jawk.frontend.AwkParser.Regexp_AST(this._enclosing, regexp);
			}

			internal AwkSymbolTableImpl(AwkParser _enclosing)
			{
				this._enclosing = _enclosing;
			}

			private readonly AwkParser _enclosing;
		}

		[System.Serializable]
		private class ParserException : System.Exception
		{
			internal ParserException(AwkParser _enclosing, string msg) : base(msg + " (line "
				 + _enclosing.reader.Line + ")")
			{
				this._enclosing = _enclosing;
			}

			private readonly AwkParser _enclosing;
			// private class AwkSymbolTableImpl
		}
	}
}
